mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	LSP rename
This commit is contained in:
		
							parent
							
								
									b6f11b3392
								
							
						
					
					
						commit
						16b64f3aee
					
				@ -159,6 +159,8 @@ set(sources
 | 
				
			|||||||
	lsp/FileRepository.h
 | 
						lsp/FileRepository.h
 | 
				
			||||||
	lsp/GotoDefinition.cpp
 | 
						lsp/GotoDefinition.cpp
 | 
				
			||||||
	lsp/GotoDefinition.h
 | 
						lsp/GotoDefinition.h
 | 
				
			||||||
 | 
						lsp/RenameSymbol.cpp
 | 
				
			||||||
 | 
						lsp/RenameSymbol.h
 | 
				
			||||||
	lsp/HandlerBase.cpp
 | 
						lsp/HandlerBase.cpp
 | 
				
			||||||
	lsp/HandlerBase.h
 | 
						lsp/HandlerBase.h
 | 
				
			||||||
	lsp/LanguageServer.cpp
 | 
						lsp/LanguageServer.cpp
 | 
				
			||||||
 | 
				
			|||||||
@ -44,7 +44,6 @@ public:
 | 
				
			|||||||
	/// Changes the source identified by the LSP client path _uri to _text.
 | 
						/// Changes the source identified by the LSP client path _uri to _text.
 | 
				
			||||||
	void setSourceByUri(std::string const& _uri, std::string _text);
 | 
						void setSourceByUri(std::string const& _uri, std::string _text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void addOrUpdateFile(boost::filesystem::path const& _path, frontend::SourceCode _source);
 | 
					 | 
				
			||||||
	void setSourceUnits(StringMap _sources);
 | 
						void setSourceUnits(StringMap _sources);
 | 
				
			||||||
	frontend::ReadCallback::Result readFile(std::string const& _kind, std::string const& _sourceUnitName);
 | 
						frontend::ReadCallback::Result readFile(std::string const& _kind, std::string const& _sourceUnitName);
 | 
				
			||||||
	frontend::ReadCallback::Callback reader()
 | 
						frontend::ReadCallback::Callback reader()
 | 
				
			||||||
 | 
				
			|||||||
@ -45,8 +45,8 @@ public:
 | 
				
			|||||||
	/// from the JSON-RPC parameters.
 | 
						/// from the JSON-RPC parameters.
 | 
				
			||||||
	std::pair<std::string, langutil::LineColumn> extractSourceUnitNameAndLineColumn(Json::Value const& _params) const;
 | 
						std::pair<std::string, langutil::LineColumn> extractSourceUnitNameAndLineColumn(Json::Value const& _params) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_server.charStreamProvider(); }
 | 
						langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_server.compilerStack(); }
 | 
				
			||||||
	FileRepository const& fileRepository() const noexcept { return m_server.fileRepository(); }
 | 
						FileRepository& fileRepository() const noexcept { return m_server.fileRepository(); }
 | 
				
			||||||
	Transport& client() const noexcept { return m_server.client(); }
 | 
						Transport& client() const noexcept { return m_server.client(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
 | 
				
			|||||||
@ -26,6 +26,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// LSP feature implementations
 | 
					// LSP feature implementations
 | 
				
			||||||
#include <libsolidity/lsp/GotoDefinition.h>
 | 
					#include <libsolidity/lsp/GotoDefinition.h>
 | 
				
			||||||
 | 
					#include <libsolidity/lsp/RenameSymbol.h>
 | 
				
			||||||
#include <libsolidity/lsp/SemanticTokensBuilder.h>
 | 
					#include <libsolidity/lsp/SemanticTokensBuilder.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <liblangutil/SourceReferenceExtractor.h>
 | 
					#include <liblangutil/SourceReferenceExtractor.h>
 | 
				
			||||||
@ -124,6 +125,7 @@ LanguageServer::LanguageServer(Transport& _transport):
 | 
				
			|||||||
		{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _2)},
 | 
							{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _2)},
 | 
				
			||||||
		{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _2)},
 | 
							{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _2)},
 | 
				
			||||||
		{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _2)},
 | 
							{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _2)},
 | 
				
			||||||
 | 
							{"textDocument/rename", RenameSymbol(*this) },
 | 
				
			||||||
		{"textDocument/implementation", GotoDefinition(*this) },
 | 
							{"textDocument/implementation", GotoDefinition(*this) },
 | 
				
			||||||
		{"textDocument/semanticTokens/full", bind(&LanguageServer::semanticTokensFull, this, _1, _2)},
 | 
							{"textDocument/semanticTokens/full", bind(&LanguageServer::semanticTokensFull, this, _1, _2)},
 | 
				
			||||||
		{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)},
 | 
							{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)},
 | 
				
			||||||
@ -314,6 +316,8 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
 | 
				
			|||||||
	replyArgs["capabilities"]["semanticTokensProvider"]["legend"] = semanticTokensLegend();
 | 
						replyArgs["capabilities"]["semanticTokensProvider"]["legend"] = semanticTokensLegend();
 | 
				
			||||||
	replyArgs["capabilities"]["semanticTokensProvider"]["range"] = false;
 | 
						replyArgs["capabilities"]["semanticTokensProvider"]["range"] = false;
 | 
				
			||||||
	replyArgs["capabilities"]["semanticTokensProvider"]["full"] = true; // XOR requests.full.delta = true
 | 
						replyArgs["capabilities"]["semanticTokensProvider"]["full"] = true; // XOR requests.full.delta = true
 | 
				
			||||||
 | 
						replyArgs["capabilities"]["renameProvider"] = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m_client.reply(_id, move(replyArgs));
 | 
						m_client.reply(_id, move(replyArgs));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -432,6 +436,7 @@ void LanguageServer::handleTextDocumentDidClose(Json::Value const& _args)
 | 
				
			|||||||
	compileAndUpdateDiagnostics();
 | 
						compileAndUpdateDiagnostics();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ASTNode const* LanguageServer::astNodeAtSourceLocation(std::string const& _sourceUnitName, LineColumn const& _filePos)
 | 
					ASTNode const* LanguageServer::astNodeAtSourceLocation(std::string const& _sourceUnitName, LineColumn const& _filePos)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (m_compilerStack.state() < CompilerStack::AnalysisPerformed)
 | 
						if (m_compilerStack.state() < CompilerStack::AnalysisPerformed)
 | 
				
			||||||
 | 
				
			|||||||
@ -33,6 +33,7 @@
 | 
				
			|||||||
namespace solidity::lsp
 | 
					namespace solidity::lsp
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RenameSymbol;
 | 
				
			||||||
enum class ErrorCode;
 | 
					enum class ErrorCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -60,7 +61,7 @@ public:
 | 
				
			|||||||
	FileRepository& fileRepository() noexcept { return m_fileRepository; }
 | 
						FileRepository& fileRepository() noexcept { return m_fileRepository; }
 | 
				
			||||||
	Transport& client() noexcept { return m_client; }
 | 
						Transport& client() noexcept { return m_client; }
 | 
				
			||||||
	frontend::ASTNode const* astNodeAtSourceLocation(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; }
 | 
						frontend::CompilerStack const& compilerStack() const noexcept { return m_compilerStack; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	/// Checks if the server is initialized (to be used by messages that need it to be initialized).
 | 
						/// Checks if the server is initialized (to be used by messages that need it to be initialized).
 | 
				
			||||||
@ -72,6 +73,7 @@ private:
 | 
				
			|||||||
	void handleTextDocumentDidOpen(Json::Value const& _args);
 | 
						void handleTextDocumentDidOpen(Json::Value const& _args);
 | 
				
			||||||
	void handleTextDocumentDidChange(Json::Value const& _args);
 | 
						void handleTextDocumentDidChange(Json::Value const& _args);
 | 
				
			||||||
	void handleTextDocumentDidClose(Json::Value const& _args);
 | 
						void handleTextDocumentDidClose(Json::Value const& _args);
 | 
				
			||||||
 | 
						void handleRename(Json::Value const& _args);
 | 
				
			||||||
	void handleGotoDefinition(MessageID _id, Json::Value const& _args);
 | 
						void handleGotoDefinition(MessageID _id, Json::Value const& _args);
 | 
				
			||||||
	void semanticTokensFull(MessageID _id, Json::Value const& _args);
 | 
						void semanticTokensFull(MessageID _id, Json::Value const& _args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										316
									
								
								libsolidity/lsp/RenameSymbol.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								libsolidity/lsp/RenameSymbol.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,316 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
						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/RenameSymbol.h>
 | 
				
			||||||
 | 
					#include <libsolidity/lsp/Utils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <libyul/AST.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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CallableDeclaration const* extractCallableDeclaration(FunctionCall const& _functionCall)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (
 | 
				
			||||||
 | 
							auto const* functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
 | 
				
			||||||
 | 
							functionType && functionType->hasDeclaration()
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
							if (auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(&functionType->declaration()))
 | 
				
			||||||
 | 
								return functionDefinition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::operator()(MessageID _id, Json::Value const& _args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto const&& [sourceUnitName, lineColumn] = extractSourceUnitNameAndLineColumn(_args);
 | 
				
			||||||
 | 
						string const newName = _args["newName"].asString();
 | 
				
			||||||
 | 
						string const uri = _args["textDocument"]["uri"].asString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ASTNode const* sourceNode = m_server.astNodeAtSourceLocation(sourceUnitName, lineColumn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m_symbolName = {};
 | 
				
			||||||
 | 
						m_declarationToRename = nullptr;
 | 
				
			||||||
 | 
						m_sourceUnits = { &m_server.compilerStack().ast(sourceUnitName) };
 | 
				
			||||||
 | 
						m_locations.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						optional<int> cursorBytePosition = charStreamProvider()
 | 
				
			||||||
 | 
							.charStream(sourceUnitName)
 | 
				
			||||||
 | 
							.translateLineColumnToPosition(lineColumn);
 | 
				
			||||||
 | 
						solAssert(cursorBytePosition.has_value(), "Expected source pos");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						extractNameAndDeclaration(*sourceNode, *cursorBytePosition);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Find all source units using this symbol
 | 
				
			||||||
 | 
						for (auto const& [name, content]: fileRepository().sourceUnits())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto const& sourceUnit = m_server.compilerStack().ast(name);
 | 
				
			||||||
 | 
							for (auto const* referencedSourceUnit: sourceUnit.referencedSourceUnits(true, util::convertContainer<set<SourceUnit const*>>(m_sourceUnits)))
 | 
				
			||||||
 | 
								if (*referencedSourceUnit->location().sourceName == sourceUnitName)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									m_sourceUnits.insert(&sourceUnit);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Origin source unit should always be checked
 | 
				
			||||||
 | 
						m_sourceUnits.insert(&m_declarationToRename->sourceUnit());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Visitor visitor(*this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (auto const* sourceUnit: m_sourceUnits)
 | 
				
			||||||
 | 
							sourceUnit->accept(visitor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Apply changes in reverse order (will iterate in reverse)
 | 
				
			||||||
 | 
						sort(m_locations.begin(), m_locations.end());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Json::Value reply = Json::objectValue;
 | 
				
			||||||
 | 
						reply["changes"] = Json::objectValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Json::Value edits = Json::arrayValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (auto i = m_locations.rbegin(); i != m_locations.rend(); i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							solAssert(i->isValid());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Replace in our file repository
 | 
				
			||||||
 | 
							string const uri = fileRepository().sourceUnitNameToUri(*i->sourceName);
 | 
				
			||||||
 | 
							string buffer = fileRepository().sourceUnits().at(*i->sourceName);
 | 
				
			||||||
 | 
							buffer.replace((size_t)i->start, (size_t)(i->end - i->start), newName);
 | 
				
			||||||
 | 
							fileRepository().setSourceByUri(uri, std::move(buffer));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Json::Value edit = Json::objectValue;
 | 
				
			||||||
 | 
							edit["range"] = toRange(*i);
 | 
				
			||||||
 | 
							edit["newText"] = newName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Record changes for the client
 | 
				
			||||||
 | 
							edits.append(edit);
 | 
				
			||||||
 | 
							if (i + 1 == m_locations.rend() || (i + 1)->sourceName != i->sourceName)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								reply["changes"][uri] = edits;
 | 
				
			||||||
 | 
								edits = Json::arrayValue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client().reply(_id, reply);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::extractNameAndDeclaration(ASTNode const& _node, int _cursorBytePosition)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Identify symbol name and node
 | 
				
			||||||
 | 
						if (auto const* declaration = dynamic_cast<Declaration const*>(&_node))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (declaration->nameLocation().containsOffset(_cursorBytePosition))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								m_symbolName = declaration->name();
 | 
				
			||||||
 | 
								m_declarationToRename = declaration;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else if (auto const* importDirective = dynamic_cast<ImportDirective const*>(declaration))
 | 
				
			||||||
 | 
								extractNameAndDeclaration(*importDirective, _cursorBytePosition);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (auto const* identifier = dynamic_cast<Identifier const*>(&_node))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (auto const* declReference = dynamic_cast<Declaration const*>(identifier->annotation().referencedDeclaration))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								m_symbolName = identifier->name();
 | 
				
			||||||
 | 
								m_declarationToRename = declReference;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (auto const* identifierPath = dynamic_cast<IdentifierPath const*>(&_node))
 | 
				
			||||||
 | 
							extractNameAndDeclaration(*identifierPath, _cursorBytePosition);
 | 
				
			||||||
 | 
						else if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(&_node))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							m_symbolName = memberAccess->memberName();
 | 
				
			||||||
 | 
							m_declarationToRename = memberAccess->annotation().referencedDeclaration;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (auto const* functionCall = dynamic_cast<FunctionCall const*>(&_node))
 | 
				
			||||||
 | 
							extractNameAndDeclaration(*functionCall, _cursorBytePosition);
 | 
				
			||||||
 | 
						else if (auto const* inlineAssembly = dynamic_cast<InlineAssembly const*>(&_node))
 | 
				
			||||||
 | 
							extractNameAndDeclaration(*inlineAssembly, _cursorBytePosition);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							solAssert(false, "Unexpected ASTNODE id: " + to_string(_node.id()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lspDebug(fmt::format("Goal: rename '{}', loc: {}-{}", m_symbolName, m_declarationToRename->nameLocation().start, m_declarationToRename->nameLocation().end));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::extractNameAndDeclaration(ImportDirective const& _importDirective, int _cursorBytePosition)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (ImportDirective::SymbolAlias const& symbolAlias: _importDirective.symbolAliases())
 | 
				
			||||||
 | 
							if (symbolAlias.location.containsOffset(_cursorBytePosition))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								solAssert(symbolAlias.alias);
 | 
				
			||||||
 | 
								m_symbolName = *symbolAlias.alias;
 | 
				
			||||||
 | 
								m_declarationToRename = symbolAlias.symbol->annotation().referencedDeclaration;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::Visitor::endVisit(ImportDirective const& _node)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Handles SourceUnit aliases
 | 
				
			||||||
 | 
						if (handleGenericDeclaration(_node))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (ImportDirective::SymbolAlias const& symbolAlias: _node.symbolAliases())
 | 
				
			||||||
 | 
							if (
 | 
				
			||||||
 | 
								symbolAlias.alias != nullptr &&
 | 
				
			||||||
 | 
								*symbolAlias.alias == m_outer.m_symbolName &&
 | 
				
			||||||
 | 
								symbolAlias.symbol->annotation().referencedDeclaration == m_outer.m_declarationToRename
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
								m_outer.m_locations.emplace_back(symbolAlias.location);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::extractNameAndDeclaration(FunctionCall const& _functionCall, int _cursorBytePosition)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (auto const* functionDefinition = extractCallableDeclaration(_functionCall))
 | 
				
			||||||
 | 
							for (size_t i = 0; i < _functionCall.names().size(); i++)
 | 
				
			||||||
 | 
								if (_functionCall.nameLocations()[i].containsOffset(_cursorBytePosition))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									m_symbolName = *_functionCall.names()[i];
 | 
				
			||||||
 | 
									for (size_t j = 0; j < functionDefinition->parameters().size(); j++)
 | 
				
			||||||
 | 
										if (
 | 
				
			||||||
 | 
											functionDefinition->parameters()[j] &&
 | 
				
			||||||
 | 
											functionDefinition->parameters()[j]->name() == m_symbolName
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
											m_declarationToRename =  functionDefinition->parameters()[j].get();
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::Visitor::endVisit(FunctionCall const& _node)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						SourceLocation nameLocationInFunctionCall;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i = 0; i < _node.names().size(); i++)
 | 
				
			||||||
 | 
							if (_node.names()[i] && *_node.names()[i] == m_outer.m_symbolName)
 | 
				
			||||||
 | 
								nameLocationInFunctionCall = _node.nameLocations()[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!nameLocationInFunctionCall.isValid())
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (auto const* functionDefinition = extractCallableDeclaration(_node))
 | 
				
			||||||
 | 
							for (size_t j = 0; j < functionDefinition->parameters().size(); j++)
 | 
				
			||||||
 | 
								if (
 | 
				
			||||||
 | 
									functionDefinition->parameters()[j] &&
 | 
				
			||||||
 | 
									*functionDefinition->parameters()[j] == *m_outer.m_declarationToRename
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
									m_outer.m_locations.emplace_back(nameLocationInFunctionCall);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::Visitor::endVisit(MemberAccess const& _node)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (
 | 
				
			||||||
 | 
							m_outer.m_symbolName == _node.memberName() &&
 | 
				
			||||||
 | 
							*m_outer.m_declarationToRename == *_node.annotation().referencedDeclaration
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
							m_outer.m_locations.emplace_back(_node.memberLocation());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::Visitor::endVisit(Identifier const& _node)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (
 | 
				
			||||||
 | 
							m_outer.m_symbolName == _node.name() &&
 | 
				
			||||||
 | 
							*m_outer.m_declarationToRename == *_node.annotation().referencedDeclaration
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
							m_outer.m_locations.emplace_back(_node.location());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::extractNameAndDeclaration(IdentifierPath const& _identifierPath, int _cursorBytePosition)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// iterate through the elements of the path to find the one the cursor is on
 | 
				
			||||||
 | 
						size_t numIdentifiers = _identifierPath.pathLocations().size();
 | 
				
			||||||
 | 
						for (size_t i = 0; i < numIdentifiers; i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto& location = _identifierPath.pathLocations()[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (location.containsOffset(_cursorBytePosition))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								solAssert(_identifierPath.annotation().pathDeclarations.size() == numIdentifiers);
 | 
				
			||||||
 | 
								solAssert(_identifierPath.path().size() == numIdentifiers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								m_declarationToRename = _identifierPath.annotation().pathDeclarations[i];
 | 
				
			||||||
 | 
								m_symbolName = _identifierPath.path()[i];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::Visitor::endVisit(IdentifierPath const& _node)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::vector<Declaration const*>& declarations = _node.annotation().pathDeclarations;
 | 
				
			||||||
 | 
						solAssert(declarations.size() == _node.path().size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i = 0; i < _node.path().size(); i++)
 | 
				
			||||||
 | 
							if (
 | 
				
			||||||
 | 
								_node.path()[i] == m_outer.m_symbolName &&
 | 
				
			||||||
 | 
								declarations[i] == m_outer.m_declarationToRename
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
								m_outer.m_locations.emplace_back(_node.pathLocations()[i]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::extractNameAndDeclaration(InlineAssembly const& _inlineAssembly, int _cursorBytePosition)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (auto&& [identifier, externalReference]: _inlineAssembly.annotation().externalReferences)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							SourceLocation location = yul::nativeLocationOf(*identifier);
 | 
				
			||||||
 | 
							location.end -= static_cast<int>(externalReference.suffix.size() + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (location.containsOffset(_cursorBytePosition))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								m_declarationToRename = externalReference.declaration;
 | 
				
			||||||
 | 
								m_symbolName = identifier->name.str();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!externalReference.suffix.empty())
 | 
				
			||||||
 | 
									m_symbolName = m_symbolName.substr(0, m_symbolName.length() - externalReference.suffix.size() - 1);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RenameSymbol::Visitor::endVisit(InlineAssembly const& _node)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (auto&& [identifier, externalReference]: _node.annotation().externalReferences)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							string identifierName = identifier->name.str();
 | 
				
			||||||
 | 
							if (!externalReference.suffix.empty())
 | 
				
			||||||
 | 
								identifierName = identifierName.substr(0, identifierName.length() - externalReference.suffix.size() - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (
 | 
				
			||||||
 | 
								externalReference.declaration == m_outer.m_declarationToRename &&
 | 
				
			||||||
 | 
								identifierName == m_outer.m_symbolName
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								SourceLocation location = yul::nativeLocationOf(*identifier);
 | 
				
			||||||
 | 
								location.end -= static_cast<int>(externalReference.suffix.size() + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								m_outer.m_locations.emplace_back(location);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										119
									
								
								libsolidity/lsp/RenameSymbol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								libsolidity/lsp/RenameSymbol.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
						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>
 | 
				
			||||||
 | 
					#include <libsolidity/ast/AST.h>
 | 
				
			||||||
 | 
					#include <libsolidity/ast/ASTVisitor.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace solidity::lsp
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RenameSymbol: public HandlerBase
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit RenameSymbol(LanguageServer& _server): HandlerBase(_server) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void operator()(MessageID, Json::Value const&);
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						// Nested class because otherwise `RenameSymbol` couldn't be easily used
 | 
				
			||||||
 | 
						// with LanguageServer::m_handlers as `ASTConstVisitor` deletes required
 | 
				
			||||||
 | 
						// c'tors
 | 
				
			||||||
 | 
						struct Visitor: public frontend::ASTConstVisitor
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							explicit Visitor(RenameSymbol& _outer): m_outer(_outer) {}
 | 
				
			||||||
 | 
							void endVisit(frontend::ImportDirective const& _node) override;
 | 
				
			||||||
 | 
							void endVisit(frontend::MemberAccess const& _node) override;
 | 
				
			||||||
 | 
							void endVisit(frontend::Identifier const& _node) override;
 | 
				
			||||||
 | 
							void endVisit(frontend::IdentifierPath const& _node) override;
 | 
				
			||||||
 | 
							void endVisit(frontend::FunctionCall const& _node) override;
 | 
				
			||||||
 | 
							void endVisit(frontend::InlineAssembly const& _node) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							void endVisit(frontend::ContractDefinition const& _node) override
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								handleGenericDeclaration(_node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							void endVisit(frontend::StructDefinition const& _node) override
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								handleGenericDeclaration(_node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							void endVisit(frontend::EnumDefinition const& _node) override
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								handleGenericDeclaration(_node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							void endVisit(frontend::EnumValue const& _node) override
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								handleGenericDeclaration(_node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							void endVisit(frontend::UserDefinedValueTypeDefinition const& _node) override
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								handleGenericDeclaration(_node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							void endVisit(frontend::VariableDeclaration const& _node) override
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								handleGenericDeclaration(_node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							void endVisit(frontend::FunctionDefinition const& _node) override
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								handleGenericDeclaration(_node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							void endVisit(frontend::ModifierDefinition const& _node) override
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								handleGenericDeclaration(_node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							void endVisit(frontend::EventDefinition const& _node) override
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								handleGenericDeclaration(_node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							void endVisit(frontend::ErrorDefinition const& _node) override
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								handleGenericDeclaration(_node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bool handleGenericDeclaration(frontend::Declaration const& _declaration)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (
 | 
				
			||||||
 | 
									m_outer.m_symbolName == _declaration.name() &&
 | 
				
			||||||
 | 
									*m_outer.m_declarationToRename == _declaration
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									m_outer.m_locations.emplace_back(_declaration.nameLocation());
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private:
 | 
				
			||||||
 | 
								RenameSymbol& m_outer;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void extractNameAndDeclaration(frontend::ASTNode const& _node, int _cursorBytePosition);
 | 
				
			||||||
 | 
						void extractNameAndDeclaration(frontend::IdentifierPath const& _identifierPath, int _cursorBytePosition);
 | 
				
			||||||
 | 
						void extractNameAndDeclaration(frontend::ImportDirective const& _importDirective, int _cursorBytePosition);
 | 
				
			||||||
 | 
						void extractNameAndDeclaration(frontend::FunctionCall const& _functionCall, int _cursorBytePosition);
 | 
				
			||||||
 | 
						void extractNameAndDeclaration(frontend::InlineAssembly const& _inlineAssembly, int _cursorBytePosition);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Node to rename
 | 
				
			||||||
 | 
						frontend::Declaration const* m_declarationToRename = nullptr;
 | 
				
			||||||
 | 
						// Original name
 | 
				
			||||||
 | 
						frontend::ASTString m_symbolName = {};
 | 
				
			||||||
 | 
						// SourceUnits to search & replace symbol in
 | 
				
			||||||
 | 
						std::set<frontend::SourceUnit const*, frontend::ASTNode::CompareByID> m_sourceUnits = {};
 | 
				
			||||||
 | 
						// Source locations that need to be replaced
 | 
				
			||||||
 | 
						std::vector<langutil::SourceLocation> m_locations = {};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -195,7 +195,7 @@ void SemanticTokensBuilder::endVisit(frontend::StructuredDocumentation const& _d
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void SemanticTokensBuilder::endVisit(frontend::Identifier const& _identifier)
 | 
					void SemanticTokensBuilder::endVisit(frontend::Identifier const& _identifier)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	lspDebug(fmt::format("Identifier: {}, {}..{} cat={}", _identifier.name(), _identifier.location().start, _identifier.location().end, _identifier.annotation().type->category()));
 | 
						//lspDebug(fmt::format("Identifier: {}, {}..{} cat={}", _identifier.name(), _identifier.location().start, _identifier.location().end, _identifier.annotation().type->category()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SemanticTokenModifiers modifiers = SemanticTokenModifiers::None;
 | 
						SemanticTokenModifiers modifiers = SemanticTokenModifiers::None;
 | 
				
			||||||
	if (_identifier.annotation().isConstant.set() && *_identifier.annotation().isConstant)
 | 
						if (_identifier.annotation().isConstant.set() && *_identifier.annotation().isConstant)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										307
									
								
								test/libsolidity/lsp/rename/contract.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								test/libsolidity/lsp/rename/contract.sol
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,307 @@
 | 
				
			|||||||
 | 
					// SPDX-License-Identifier: UNLICENSED
 | 
				
			||||||
 | 
					pragma solidity >=0.8.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					contract ToRename
 | 
				
			||||||
 | 
					//       ^ @CursorOnContractDefinition
 | 
				
			||||||
 | 
					//       ^^^^^^^^ @ContractInDefinition
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					contract User
 | 
				
			||||||
 | 
					//       ^^^^ @UserContractInContractTest
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ToRename public publicVariable;
 | 
				
			||||||
 | 
					//  ^^^^^^^^ @ContractInPublicVariable
 | 
				
			||||||
 | 
					//         ^ @CursorOnPublicVariableType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ToRename[10] previousContracts;
 | 
				
			||||||
 | 
					//  ^^^^^^^^ @ContractInArrayType
 | 
				
			||||||
 | 
					//   ^ @CursorOnArrayType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mapping(int => ToRename) contractMapping;
 | 
				
			||||||
 | 
					    //             ^^^^^^^^ @ContractInMapping
 | 
				
			||||||
 | 
					    //              ^ @CursorOnMapping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function getContract() public returns (ToRename)
 | 
				
			||||||
 | 
					                                  //       ^^^^^^^^ @ContractInReturnParameter
 | 
				
			||||||
 | 
					//                                            ^ @CursorOnReturnParameter
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return new ToRename();
 | 
				
			||||||
 | 
					        //         ^^^^^^^^ @ContractInReturnExpression
 | 
				
			||||||
 | 
					//                    ^ @CursorOnReturnExpression
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function setContract(ToRename _contract) public
 | 
				
			||||||
 | 
					    //                   ^^^^^^^^ @ContractInParameter
 | 
				
			||||||
 | 
					    //                        ^ @CursorOnParameter
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        publicVariable = _contract;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// ----
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnContractDefinition
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnExpression
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInMapping
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInArrayType
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnReturnParameter
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnExpression
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInMapping
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInArrayType
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnReturnExpression
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnExpression
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInMapping
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInArrayType
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnPublicVariableType
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnExpression
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInMapping
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInArrayType
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnArrayType
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnExpression
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInMapping
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInArrayType
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnMapping
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnExpression
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInMapping
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInArrayType
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnParameter
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnExpression
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInMapping
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInArrayType
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
							
								
								
									
										194
									
								
								test/libsolidity/lsp/rename/function.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								test/libsolidity/lsp/rename/function.sol
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,194 @@
 | 
				
			|||||||
 | 
					// SPDX-License-Identifier: UNLICENSED
 | 
				
			||||||
 | 
					pragma solidity >=0.8.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					contract C
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    function renameMe() public pure returns (int)
 | 
				
			||||||
 | 
					    //       ^^^^^^^^ @FunctionInDefinition
 | 
				
			||||||
 | 
					    //            ^ @CursorInDefinition
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function other() public view
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        renameMe();
 | 
				
			||||||
 | 
					//      ^^^^^^^^ @FunctionInFunctionSameContract
 | 
				
			||||||
 | 
					//         ^ @CursorInFunctionSameContract
 | 
				
			||||||
 | 
					        this.renameMe();
 | 
				
			||||||
 | 
					     //      ^^^^^^^^ @FunctionInFunctionSameContractExternal
 | 
				
			||||||
 | 
					         //         ^ @CursorInFunctionSameContractExternal
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					contract Other
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    C m_contract;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function other() public view
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        m_contract.renameMe();
 | 
				
			||||||
 | 
					           //      ^^^^^^^^ @FunctionInFunctionOtherContract
 | 
				
			||||||
 | 
					        //         ^ @CursorInFunctionOtherContract
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function free() pure
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    C local_contract;
 | 
				
			||||||
 | 
					    local_contract.renameMe();
 | 
				
			||||||
 | 
					           //      ^^^^^^^^ @FunctionInFreeFunction
 | 
				
			||||||
 | 
					        //         ^ @CursorInFreeFunction
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ----
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorInDefinition
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/function.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFreeFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionOtherContract
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionSameContractExternal
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionSameContract
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorInFunctionOtherContract
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/function.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFreeFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionOtherContract
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionSameContractExternal
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionSameContract
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorInFunctionSameContractExternal
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/function.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFreeFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionOtherContract
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionSameContractExternal
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionSameContract
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorInFunctionSameContract
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/function.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFreeFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionOtherContract
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionSameContractExternal
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionSameContract
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorInFreeFunction
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/function.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFreeFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionOtherContract
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionSameContractExternal
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInFunctionSameContract
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FunctionInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
							
								
								
									
										202
									
								
								test/libsolidity/lsp/rename/functionCall.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								test/libsolidity/lsp/rename/functionCall.sol
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					// SPDX-License-Identifier: UNLICENSED
 | 
				
			||||||
 | 
					pragma solidity >=0.8.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					contract C
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    function foo(int a, int b, int c) pure public returns(int)
 | 
				
			||||||
 | 
					    //                      ^ @ParameterB
 | 
				
			||||||
 | 
					    //               ^ @ParameterA
 | 
				
			||||||
 | 
					    //                             ^ @ParameterC
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return a + b + c;
 | 
				
			||||||
 | 
					        //         ^ @ParameterBInFoo
 | 
				
			||||||
 | 
					        //     ^ @ParameterAInFoo
 | 
				
			||||||
 | 
					        //             ^ @ParameterCInFoo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function bar() public view
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this.foo({c:1, b:2, a:3});
 | 
				
			||||||
 | 
					        //             ^ @ParameterBInCall
 | 
				
			||||||
 | 
					        //        ^ @ParameterCInCall
 | 
				
			||||||
 | 
					        //                  ^ @ParameterAInCall
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// ----
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @ParameterA
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/functionCall.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterAInCall
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterAInFoo
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterA
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @ParameterAInCall
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/functionCall.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterAInCall
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterAInFoo
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterA
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @ParameterAInFoo
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/functionCall.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterAInCall
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterAInFoo
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterA
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @ParameterC
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/functionCall.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterCInCall
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterCInFoo
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterC
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @ParameterCInCall
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/functionCall.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterCInCall
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterCInFoo
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterC
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @ParameterCInFoo
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/functionCall.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterCInCall
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterCInFoo
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterC
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @ParameterBInCall
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/functionCall.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterBInCall
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterBInFoo
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterB
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @ParameterBInFoo
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/functionCall.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterBInCall
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterBInFoo
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ParameterB
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
							
								
								
									
										247
									
								
								test/libsolidity/lsp/rename/import_directive.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								test/libsolidity/lsp/rename/import_directive.sol
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,247 @@
 | 
				
			|||||||
 | 
					// SPDX-License-Identifier: UNLICENSED
 | 
				
			||||||
 | 
					pragma solidity >=0.8.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./contract.sol" as externalFile;
 | 
				
			||||||
 | 
					//                         ^^^^^^^^^^^^ @FileAliasInImportDirective
 | 
				
			||||||
 | 
					//                             ^ @CursorOnFileAliasInImportDirective
 | 
				
			||||||
 | 
					import {ToRename as ExternalContract, User} from "./contract.sol";
 | 
				
			||||||
 | 
					//                  ^^^^^^^^^^^^^^^^ @RenamedContractInImportDirective
 | 
				
			||||||
 | 
					//                         ^ @CursorOnRenamedContractInImportDirective
 | 
				
			||||||
 | 
					//      ^^^^^^^^ @OriginalNameInImportDirective
 | 
				
			||||||
 | 
					//      ^ @CursorOnOriginalNameInImportDirective
 | 
				
			||||||
 | 
					//                                    ^^^^ @UserInImportDirective
 | 
				
			||||||
 | 
					//                                    ^ @CursorOnUserInImportDirective
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					contract C
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ExternalContract public externalContract;
 | 
				
			||||||
 | 
					//  ^^^^^^^^^^^^^^^^ @RenamedContractInPublicVariable
 | 
				
			||||||
 | 
					//        ^ @CursorOnRenamedContractInPublicVariable
 | 
				
			||||||
 | 
					    externalFile.ToRename public externalFileContract;
 | 
				
			||||||
 | 
					//  ^^^^^^^^^^^^ @FileAliasInPublicVariable
 | 
				
			||||||
 | 
					//  ^ @CursorOnFileAliasInPublicVariable
 | 
				
			||||||
 | 
					//               ^^^^^^^^ @OriginalNameInPublicVariable
 | 
				
			||||||
 | 
					//                      ^ @CursorOnOriginalNameInPublicVariable
 | 
				
			||||||
 | 
					    User public externalUserContract;
 | 
				
			||||||
 | 
					//  ^^^^ @UserInPublicVariable
 | 
				
			||||||
 | 
					//     ^ @CursorOnUserInPublicVariable
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ----
 | 
				
			||||||
 | 
					// contract:
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnFileAliasInImportDirective
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/import_directive.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FileAliasInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FileAliasInImportDirective
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnRenamedContractInImportDirective
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/import_directive.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @RenamedContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @RenamedContractInImportDirective
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnOriginalNameInImportDirective
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnExpression
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInMapping
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInArrayType
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ],
 | 
				
			||||||
 | 
					//         "rename/import_directive.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @OriginalNameInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @OriginalNameInImportDirective
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnRenamedContractInPublicVariable
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/import_directive.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @RenamedContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @RenamedContractInImportDirective
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnFileAliasInPublicVariable
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/import_directive.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FileAliasInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @FileAliasInImportDirective
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnOriginalNameInPublicVariable
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnExpression
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInReturnParameter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInMapping
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInArrayType
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @ContractInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ],
 | 
				
			||||||
 | 
					//         "rename/import_directive.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @OriginalNameInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @OriginalNameInImportDirective
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnUserInPublicVariable
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @UserContractInContractTest
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ],
 | 
				
			||||||
 | 
					//         "rename/import_directive.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @UserInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @UserInImportDirective
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnUserInImportDirective
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/contract.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @UserContractInContractTest
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ],
 | 
				
			||||||
 | 
					//         "rename/import_directive.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @UserInPublicVariable
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @UserInImportDirective
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
							
								
								
									
										132
									
								
								test/libsolidity/lsp/rename/variable.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								test/libsolidity/lsp/rename/variable.sol
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					// SPDX-License-Identifier: UNLICENSED
 | 
				
			||||||
 | 
					pragma solidity >=0.8.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					contract C
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int public renameMe;
 | 
				
			||||||
 | 
					    //         ^^^^^^^^ @VariableInDefinition
 | 
				
			||||||
 | 
					    //         ^ @CursorOnVariableDefinition
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function foo() public returns(int)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        renameMe = 1;
 | 
				
			||||||
 | 
					//      ^^^^^^^^ @VariableInFunction
 | 
				
			||||||
 | 
					//             ^ @CursorOnVariableInFunction
 | 
				
			||||||
 | 
					        return this.renameMe();
 | 
				
			||||||
 | 
					//                  ^^^^^^^^ @VariableInGetter
 | 
				
			||||||
 | 
					//                     ^ @CursorOnVariableInGetter
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function freeFunction(C _contract) view returns(int)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return _contract.renameMe();
 | 
				
			||||||
 | 
					    //               ^^^^^^^^ @VariableInFreeFunction
 | 
				
			||||||
 | 
					    //                 ^ @CursorOnVariableInFreeFunction
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ----
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnVariableInFunction
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/variable.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInFreeFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInGetter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnVariableDefinition
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/variable.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInFreeFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInGetter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnVariableInGetter
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/variable.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInFreeFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInGetter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// -> textDocument/rename {
 | 
				
			||||||
 | 
					//     "newName": "Renamed",
 | 
				
			||||||
 | 
					//     "position": @CursorOnVariableInFreeFunction
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					// <- {
 | 
				
			||||||
 | 
					//     "changes": {
 | 
				
			||||||
 | 
					//         "rename/variable.sol": [
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInFreeFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInGetter
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInFunction
 | 
				
			||||||
 | 
					//             },
 | 
				
			||||||
 | 
					//             {
 | 
				
			||||||
 | 
					//                 "newText": "Renamed",
 | 
				
			||||||
 | 
					//                 "range": @VariableInDefinition
 | 
				
			||||||
 | 
					//             }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
							
								
								
									
										154
									
								
								test/lsp.py
									
									
									
									
									
								
							
							
						
						
									
										154
									
								
								test/lsp.py
									
									
									
									
									
								
							@ -15,7 +15,7 @@ from copy import deepcopy
 | 
				
			|||||||
from enum import Enum, auto
 | 
					from enum import Enum, auto
 | 
				
			||||||
from itertools import islice
 | 
					from itertools import islice
 | 
				
			||||||
from pathlib import PurePath
 | 
					from pathlib import PurePath
 | 
				
			||||||
from typing import Any, List, Optional, Tuple, Union
 | 
					from typing import Any, List, Optional, Tuple, Union, NewType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import colorama  # Enables the use of SGR & CUP terminal VT sequences on Windows.
 | 
					import colorama  # Enables the use of SGR & CUP terminal VT sequences on Windows.
 | 
				
			||||||
from deepdiff import DeepDiff
 | 
					from deepdiff import DeepDiff
 | 
				
			||||||
@ -30,6 +30,13 @@ else:
 | 
				
			|||||||
    tty.setcbreak(sys.stdin.fileno())
 | 
					    tty.setcbreak(sys.stdin.fileno())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Type for the pure test name without .sol suffix or sub directory
 | 
				
			||||||
 | 
					TestName = NewType("TestName", str)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Type for the test path, e.g.  subdir/mytest.sol
 | 
				
			||||||
 | 
					RelativeTestPath = NewType("RelativeTestPath", str)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def escape_string(text: str) -> str:
 | 
					def escape_string(text: str) -> str:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Trivially escapes given input string's \r \n and \\.
 | 
					    Trivially escapes given input string's \r \n and \\.
 | 
				
			||||||
@ -148,11 +155,13 @@ class JsonRpcProcess:
 | 
				
			|||||||
    exe_args: List[str]
 | 
					    exe_args: List[str]
 | 
				
			||||||
    process: subprocess.Popen
 | 
					    process: subprocess.Popen
 | 
				
			||||||
    trace_io: bool
 | 
					    trace_io: bool
 | 
				
			||||||
 | 
					    print_pid: bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, exe_path: str, exe_args: List[str], trace_io: bool = True):
 | 
					    def __init__(self, exe_path: str, exe_args: List[str], trace_io: bool = True, print_pid = False):
 | 
				
			||||||
        self.exe_path = exe_path
 | 
					        self.exe_path = exe_path
 | 
				
			||||||
        self.exe_args = exe_args
 | 
					        self.exe_args = exe_args
 | 
				
			||||||
        self.trace_io = trace_io
 | 
					        self.trace_io = trace_io
 | 
				
			||||||
 | 
					        self.print_pid = print_pid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __enter__(self):
 | 
					    def __enter__(self):
 | 
				
			||||||
        self.process = subprocess.Popen(
 | 
					        self.process = subprocess.Popen(
 | 
				
			||||||
@ -161,6 +170,10 @@ class JsonRpcProcess:
 | 
				
			|||||||
            stdout=subprocess.PIPE,
 | 
					            stdout=subprocess.PIPE,
 | 
				
			||||||
            stderr=subprocess.PIPE
 | 
					            stderr=subprocess.PIPE
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.print_pid:
 | 
				
			||||||
 | 
					            print(f"solc pid: {self.process.pid}. Attach with sudo gdb -p {self.process.pid}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return self
 | 
					        return self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __exit__(self, exception_type, exception_value, traceback) -> None:
 | 
					    def __exit__(self, exception_type, exception_value, traceback) -> None:
 | 
				
			||||||
@ -285,6 +298,13 @@ def create_cli_parser() -> argparse.ArgumentParser:
 | 
				
			|||||||
        action="store_true",
 | 
					        action="store_true",
 | 
				
			||||||
        help="Prevent interactive queries and just fail instead."
 | 
					        help="Prevent interactive queries and just fail instead."
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    parser.set_defaults(print_solc_pid=False)
 | 
				
			||||||
 | 
					    parser.add_argument(
 | 
				
			||||||
 | 
					        "-p", "--print-solc-pid",
 | 
				
			||||||
 | 
					        dest="print_solc_pid",
 | 
				
			||||||
 | 
					        action="store_true",
 | 
				
			||||||
 | 
					        help="Print pid of each started solc for debugging purposes."
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    parser.set_defaults(trace_io=False)
 | 
					    parser.set_defaults(trace_io=False)
 | 
				
			||||||
    parser.add_argument(
 | 
					    parser.add_argument(
 | 
				
			||||||
        "-T", "--trace-io",
 | 
					        "-T", "--trace-io",
 | 
				
			||||||
@ -347,10 +367,13 @@ class TestParser:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    parsed_testcases = TestParser(content).parse()
 | 
					    parsed_testcases = TestParser(content).parse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # First diagnostics are yielded
 | 
					    # First diagnostics are yielded.
 | 
				
			||||||
 | 
					    # Type is "TestParser.Diagnostics"
 | 
				
			||||||
    expected_diagnostics = next(parsed_testcases)
 | 
					    expected_diagnostics = next(parsed_testcases)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ...
 | 
					    ...
 | 
				
			||||||
    # Now each request/response pair in the test definition
 | 
					    # Now each request/response pair in the test definition
 | 
				
			||||||
 | 
					    # Type is "TestParser.RequestAndResponse"
 | 
				
			||||||
    for testcase in self.parsed_testcases:
 | 
					    for testcase in self.parsed_testcases:
 | 
				
			||||||
        ...
 | 
					        ...
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
@ -393,11 +416,11 @@ class TestParser:
 | 
				
			|||||||
        yield self.parseDiagnostics()
 | 
					        yield self.parseDiagnostics()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while not self.at_end():
 | 
					        while not self.at_end():
 | 
				
			||||||
            yield self.RequestAndResponse(**self.parseRequestAndResponse())
 | 
					            yield self.parseRequestAndResponse()
 | 
				
			||||||
            self.next_line()
 | 
					            self.next_line()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def parseDiagnostics(self):
 | 
					    def parseDiagnostics(self) -> Diagnostics:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Parse diagnostic expectations specified in the file.
 | 
					        Parse diagnostic expectations specified in the file.
 | 
				
			||||||
        Returns a named tuple instance of "Diagnostics"
 | 
					        Returns a named tuple instance of "Diagnostics"
 | 
				
			||||||
@ -429,7 +452,7 @@ class TestParser:
 | 
				
			|||||||
        return self.Diagnostics(**diagnostics)
 | 
					        return self.Diagnostics(**diagnostics)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def parseRequestAndResponse(self):
 | 
					    def parseRequestAndResponse(self) -> RequestAndResponse:
 | 
				
			||||||
        RESPONSE_START = "// <- "
 | 
					        RESPONSE_START = "// <- "
 | 
				
			||||||
        REQUEST_END = "// }"
 | 
					        REQUEST_END = "// }"
 | 
				
			||||||
        COMMENT_PREFIX = "// "
 | 
					        COMMENT_PREFIX = "// "
 | 
				
			||||||
@ -490,7 +513,7 @@ class TestParser:
 | 
				
			|||||||
            if self.at_end():
 | 
					            if self.at_end():
 | 
				
			||||||
                raise TestParserException(ret, "Response footer not found")
 | 
					                raise TestParserException(ret, "Response footer not found")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return ret
 | 
					        return self.RequestAndResponse(**ret)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def next_line(self):
 | 
					    def next_line(self):
 | 
				
			||||||
        self.current_line_tuple = next(self.lines, None)
 | 
					        self.current_line_tuple = next(self.lines, None)
 | 
				
			||||||
@ -532,7 +555,7 @@ class FileTestRunner:
 | 
				
			|||||||
        self.solc = solc
 | 
					        self.solc = solc
 | 
				
			||||||
        self.open_tests = []
 | 
					        self.open_tests = []
 | 
				
			||||||
        self.content = self.suite.get_test_file_contents(self.test_name, self.sub_dir)
 | 
					        self.content = self.suite.get_test_file_contents(self.test_name, self.sub_dir)
 | 
				
			||||||
        self.markers = self.suite.get_file_tags(self.test_name, self.sub_dir)
 | 
					        self.markers = self.suite.get_test_tags(self.test_name, self.sub_dir)
 | 
				
			||||||
        self.parsed_testcases = None
 | 
					        self.parsed_testcases = None
 | 
				
			||||||
        self.expected_diagnostics = None
 | 
					        self.expected_diagnostics = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -580,7 +603,7 @@ class FileTestRunner:
 | 
				
			|||||||
                    len(expected_diagnostics),
 | 
					                    len(expected_diagnostics),
 | 
				
			||||||
                    description="Unexpected amount of diagnostics"
 | 
					                    description="Unexpected amount of diagnostics"
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                markers = self.suite.get_file_tags(testname, sub_dir)
 | 
					                markers = self.suite.get_test_tags(testname, sub_dir)
 | 
				
			||||||
                for actual_diagnostic in diagnostics_per_file["diagnostics"]:
 | 
					                for actual_diagnostic in diagnostics_per_file["diagnostics"]:
 | 
				
			||||||
                    expected_diagnostic = next((diagnostic for diagnostic in
 | 
					                    expected_diagnostic = next((diagnostic for diagnostic in
 | 
				
			||||||
                        expected_diagnostics if actual_diagnostic['range'] ==
 | 
					                        expected_diagnostics if actual_diagnostic['range'] ==
 | 
				
			||||||
@ -643,7 +666,13 @@ class FileTestRunner:
 | 
				
			|||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            self.close_all_open_files()
 | 
					            self.close_all_open_files()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def user_interaction_failed_method_test(self, testcase, actual, expected) -> TestResult:
 | 
					    def user_interaction_failed_method_test(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        testcase: TestParser.RequestAndResponse,
 | 
				
			||||||
 | 
					        actual,
 | 
				
			||||||
 | 
					        expected
 | 
				
			||||||
 | 
					    ) -> TestResult:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        actual_pretty = self.suite.replace_ranges_with_tags(actual, self.sub_dir)
 | 
					        actual_pretty = self.suite.replace_ranges_with_tags(actual, self.sub_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if expected is None:
 | 
					        if expected is None:
 | 
				
			||||||
@ -688,16 +717,29 @@ class FileTestRunner:
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Runs the given testcase.
 | 
					        Runs the given testcase.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        requestBodyJson = self.parse_json_with_tags(testcase.request, self.markers)
 | 
					        requestBodyJson = self.parse_json_with_tags(testcase.request, self.markers)
 | 
				
			||||||
        # add textDocument/uri if missing
 | 
					        # add textDocument/uri if missing
 | 
				
			||||||
        if 'textDocument' not in requestBodyJson:
 | 
					        if 'textDocument' not in requestBodyJson:
 | 
				
			||||||
            requestBodyJson['textDocument'] = { 'uri': self.suite.get_test_file_uri(self.test_name, self.sub_dir) }
 | 
					            requestBodyJson['textDocument'] = { 'uri': self.suite.get_test_file_uri(self.test_name, self.sub_dir) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        actualResponseJson = self.solc.call_method(testcase.method, requestBodyJson)
 | 
					        actualResponseJson = self.solc.call_method(testcase.method, requestBodyJson)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # simplify response
 | 
					        # simplify response
 | 
				
			||||||
 | 
					        if "result" in actualResponseJson:
 | 
				
			||||||
 | 
					            if isinstance(actualResponseJson["result"], list):
 | 
				
			||||||
                for result in actualResponseJson["result"]:
 | 
					                for result in actualResponseJson["result"]:
 | 
				
			||||||
                    if "uri" in result:
 | 
					                    if "uri" in result:
 | 
				
			||||||
                        result["uri"] = result["uri"].replace(self.suite.project_root_uri + "/" + self.sub_dir + "/", "")
 | 
					                        result["uri"] = result["uri"].replace(self.suite.project_root_uri + "/" + self.sub_dir + "/", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            elif isinstance(actualResponseJson["result"], dict):
 | 
				
			||||||
 | 
					                if "changes" in actualResponseJson["result"]:
 | 
				
			||||||
 | 
					                    changes = actualResponseJson["result"]["changes"]
 | 
				
			||||||
 | 
					                    for key in list(changes.keys()):
 | 
				
			||||||
 | 
					                        new_key = key.replace(self.suite.project_root_uri + "/", "")
 | 
				
			||||||
 | 
					                        changes[new_key] = changes[key]
 | 
				
			||||||
 | 
					                        del changes[key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if "jsonrpc" in actualResponseJson:
 | 
					        if "jsonrpc" in actualResponseJson:
 | 
				
			||||||
            actualResponseJson.pop("jsonrpc")
 | 
					            actualResponseJson.pop("jsonrpc")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -737,21 +779,39 @@ class FileTestRunner:
 | 
				
			|||||||
            if not isinstance(data, dict):
 | 
					            if not isinstance(data, dict):
 | 
				
			||||||
                return data
 | 
					                return data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def findMarker(desired_tag):
 | 
				
			||||||
 | 
					                if not isinstance(desired_tag, str):
 | 
				
			||||||
 | 
					                    return desired_tag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for tag, tagRange in markers.items():
 | 
				
			||||||
 | 
					                    if tag == desired_tag:
 | 
				
			||||||
 | 
					                        return tagRange
 | 
				
			||||||
 | 
					                    elif tag.lower() == desired_tag.lower():
 | 
				
			||||||
 | 
					                        raise Exception(f"Detected lower/upper case mismatch: Requested {desired_tag} but only found {tag}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                raise Exception(f"Marker {desired_tag} not found in file")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Check if we need markers from a specific file
 | 
					            # Check if we need markers from a specific file
 | 
				
			||||||
            # Needs to be done before the loop or it might be called only after
 | 
					            # Needs to be done before the loop or it might be called only after
 | 
				
			||||||
            # we found "range" or "position"
 | 
					            # we found "range" or "position"
 | 
				
			||||||
            if "uri" in data:
 | 
					            if "uri" in data:
 | 
				
			||||||
                markers = self.suite.get_file_tags(data["uri"][:-len(".sol")], self.sub_dir)
 | 
					                markers = self.suite.get_test_tags(data["uri"][:-len(".sol")], self.sub_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for key, val in data.items():
 | 
					            for key, val in data.items():
 | 
				
			||||||
                if key == "range":
 | 
					                if key == "range":
 | 
				
			||||||
                    for tag, tagRange in markers.items():
 | 
					                    data[key] = findMarker(val)
 | 
				
			||||||
                        if tag == val:
 | 
					 | 
				
			||||||
                            data[key] = tagRange
 | 
					 | 
				
			||||||
                elif key == "position":
 | 
					                elif key == "position":
 | 
				
			||||||
                    for tag, tagRange in markers.items():
 | 
					                    tag_range = findMarker(val)
 | 
				
			||||||
                        if tag == val:
 | 
					                    if "start" in tag_range:
 | 
				
			||||||
                            data[key] = tagRange["start"]
 | 
					                        data[key] = tag_range["start"]
 | 
				
			||||||
 | 
					                elif key == "changes":
 | 
				
			||||||
 | 
					                    for path, list_of_changes in val.items():
 | 
				
			||||||
 | 
					                        test_name, file_sub_dir = split_path(path)
 | 
				
			||||||
 | 
					                        markers = self.suite.get_test_tags(test_name[:-len(".sol")], file_sub_dir)
 | 
				
			||||||
 | 
					                        for change in list_of_changes:
 | 
				
			||||||
 | 
					                            if "range" in change:
 | 
				
			||||||
 | 
					                                change["range"] = findMarker(change["range"])
 | 
				
			||||||
                elif isinstance(val, dict):
 | 
					                elif isinstance(val, dict):
 | 
				
			||||||
                    replace_tag(val, markers)
 | 
					                    replace_tag(val, markers)
 | 
				
			||||||
                elif isinstance(val, list):
 | 
					                elif isinstance(val, list):
 | 
				
			||||||
@ -781,6 +841,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
        self.test_pattern = args.test_pattern
 | 
					        self.test_pattern = args.test_pattern
 | 
				
			||||||
        self.fail_fast = args.fail_fast
 | 
					        self.fail_fast = args.fail_fast
 | 
				
			||||||
        self.non_interactive = args.non_interactive
 | 
					        self.non_interactive = args.non_interactive
 | 
				
			||||||
 | 
					        self.print_solc_pid = args.print_solc_pid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        print(f"{SGR_NOTICE}test pattern: {self.test_pattern}{SGR_RESET}")
 | 
					        print(f"{SGR_NOTICE}test pattern: {self.test_pattern}{SGR_RESET}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -803,7 +864,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
            title: str = test_fn.__name__[5:]
 | 
					            title: str = test_fn.__name__[5:]
 | 
				
			||||||
            print(f"{SGR_TEST_BEGIN}Testing {title} ...{SGR_RESET}")
 | 
					            print(f"{SGR_TEST_BEGIN}Testing {title} ...{SGR_RESET}")
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                with JsonRpcProcess(self.solc_path, ["--lsp"], trace_io=self.trace_io) as solc:
 | 
					                with JsonRpcProcess(self.solc_path, ["--lsp"], trace_io=self.trace_io, print_pid=self.print_solc_pid) as solc:
 | 
				
			||||||
                    test_fn(solc)
 | 
					                    test_fn(solc)
 | 
				
			||||||
                    self.test_counter.passed += 1
 | 
					                    self.test_counter.passed += 1
 | 
				
			||||||
            except ExpectationFailed:
 | 
					            except ExpectationFailed:
 | 
				
			||||||
@ -1102,7 +1163,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
        Find and return the tag that represents the requested range otherwise
 | 
					        Find and return the tag that represents the requested range otherwise
 | 
				
			||||||
        return None.
 | 
					        return None.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        markers = self.get_file_tags(test, sub_dir)
 | 
					        markers = self.get_test_tags(test, sub_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for tag, tag_range in markers.items():
 | 
					        for tag, tag_range in markers.items():
 | 
				
			||||||
            if tag_range == target_range:
 | 
					            if tag_range == target_range:
 | 
				
			||||||
@ -1113,8 +1174,18 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
    def replace_ranges_with_tags(self, content, sub_dir):
 | 
					    def replace_ranges_with_tags(self, content, sub_dir):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Replace matching ranges with "@<tagname>".
 | 
					        Replace matching ranges with "@<tagname>".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Recognized patterns:
 | 
				
			||||||
 | 
					            { "changes": { "<uri>": { "range": "<range>" } } }
 | 
				
			||||||
 | 
					            { "uri": "<uri>", "range": "<range> }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def replace_range(item: dict, markers):
 | 
				
			||||||
 | 
					            for tag, tagRange in markers.items():
 | 
				
			||||||
 | 
					                if "range" in item and tagRange == item["range"]:
 | 
				
			||||||
 | 
					                    item["range"] = str(tag)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def recursive_iter(obj):
 | 
					        def recursive_iter(obj):
 | 
				
			||||||
            if isinstance(obj, dict):
 | 
					            if isinstance(obj, dict):
 | 
				
			||||||
                yield obj
 | 
					                yield obj
 | 
				
			||||||
@ -1126,10 +1197,27 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        for item in recursive_iter(content):
 | 
					        for item in recursive_iter(content):
 | 
				
			||||||
            if "uri" in item and "range" in item:
 | 
					            if "uri" in item and "range" in item:
 | 
				
			||||||
                markers = self.get_file_tags(item["uri"][:-len(".sol")], sub_dir)
 | 
					                try:
 | 
				
			||||||
                for tag, tagRange in markers.items():
 | 
					                    markers = self.get_test_tags(item["uri"][:-len(".sol")], sub_dir)
 | 
				
			||||||
                    if tagRange == item["range"]:
 | 
					                    replace_range(item, markers)
 | 
				
			||||||
                        item["range"] = str(tag)
 | 
					                except FileNotFoundError:
 | 
				
			||||||
 | 
					                    # Skip over errors as this is user provided input that can
 | 
				
			||||||
 | 
					                    # point to non-existing files
 | 
				
			||||||
 | 
					                    pass
 | 
				
			||||||
 | 
					            elif "changes" in item:
 | 
				
			||||||
 | 
					                for file, changes_for_file in item["changes"].items():
 | 
				
			||||||
 | 
					                    test_name, file_sub_dir = split_path(file)
 | 
				
			||||||
 | 
					                    try:
 | 
				
			||||||
 | 
					                        markers = self.get_test_tags(test_name[:-len(".sol")], file_sub_dir)
 | 
				
			||||||
 | 
					                        for change in changes_for_file:
 | 
				
			||||||
 | 
					                            replace_range(change, markers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    except FileNotFoundError:
 | 
				
			||||||
 | 
					                        # Skip over errors as this is user provided input that can
 | 
				
			||||||
 | 
					                        # point to non-existing files
 | 
				
			||||||
 | 
					                        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Convert JSON to string and split it at the quoted tags
 | 
					        # Convert JSON to string and split it at the quoted tags
 | 
				
			||||||
        split_by_tag = TEST_REGEXES.findQuotedTag.split(json.dumps(content, indent=4, sort_keys=True))
 | 
					        split_by_tag = TEST_REGEXES.findQuotedTag.split(json.dumps(content, indent=4, sort_keys=True))
 | 
				
			||||||
@ -1178,7 +1266,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
        if user_response == "r":
 | 
					        if user_response == "r":
 | 
				
			||||||
            print("retrying...")
 | 
					            print("retrying...")
 | 
				
			||||||
            # pragma pylint: disable=no-member
 | 
					            # pragma pylint: disable=no-member
 | 
				
			||||||
            self.get_file_tags.cache_clear()
 | 
					            self.get_test_tags.cache_clear()
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        if user_response == "e":
 | 
					        if user_response == "e":
 | 
				
			||||||
            editor = os.environ.get('VISUAL', os.environ.get('EDITOR', 'vi'))
 | 
					            editor = os.environ.get('VISUAL', os.environ.get('EDITOR', 'vi'))
 | 
				
			||||||
@ -1188,7 +1276,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
                check=True
 | 
					                check=True
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            # pragma pylint: disable=no-member
 | 
					            # pragma pylint: disable=no-member
 | 
				
			||||||
            self.get_file_tags.cache_clear()
 | 
					            self.get_test_tags.cache_clear()
 | 
				
			||||||
        elif user_response == "s":
 | 
					        elif user_response == "s":
 | 
				
			||||||
            print("skipping...")
 | 
					            print("skipping...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1236,11 +1324,11 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
        report = published_diagnostics[1]
 | 
					        report = published_diagnostics[1]
 | 
				
			||||||
        self.expect_equal(report['uri'], self.get_test_file_uri('lib', 'goto'), "Correct file URI")
 | 
					        self.expect_equal(report['uri'], self.get_test_file_uri('lib', 'goto'), "Correct file URI")
 | 
				
			||||||
        self.expect_equal(len(report['diagnostics']), 1, "one diagnostic")
 | 
					        self.expect_equal(len(report['diagnostics']), 1, "one diagnostic")
 | 
				
			||||||
        marker = self.get_file_tags("lib", "goto")["@diagnostics"]
 | 
					        marker = self.get_test_tags("lib", "goto")["@diagnostics"]
 | 
				
			||||||
        self.expect_diagnostic(report['diagnostics'][0], code=2072, marker=marker)
 | 
					        self.expect_diagnostic(report['diagnostics'][0], code=2072, marker=marker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @functools.lru_cache() # pragma pylint: disable=lru-cache-decorating-method
 | 
					    @functools.lru_cache() # pragma pylint: disable=lru-cache-decorating-method
 | 
				
			||||||
    def get_file_tags(self, test_name: str, sub_dir=None, verbose=False):
 | 
					    def get_test_tags(self, test_name: TestName, sub_dir=None, verbose=False):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Finds all tags (e.g. @tagname) in the given test and returns them as a
 | 
					        Finds all tags (e.g. @tagname) in the given test and returns them as a
 | 
				
			||||||
        dictionary having the following structure: {
 | 
					        dictionary having the following structure: {
 | 
				
			||||||
@ -1285,7 +1373,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
    def test_didChange_in_A_causing_error_in_B(self, solc: JsonRpcProcess) -> None:
 | 
					    def test_didChange_in_A_causing_error_in_B(self, solc: JsonRpcProcess) -> None:
 | 
				
			||||||
        # Reusing another test but now change some file that generates an error in the other.
 | 
					        # Reusing another test but now change some file that generates an error in the other.
 | 
				
			||||||
        self.test_textDocument_didOpen_with_relative_import(solc)
 | 
					        self.test_textDocument_didOpen_with_relative_import(solc)
 | 
				
			||||||
        marker = self.get_file_tags("lib", "goto")["@addFunction"]
 | 
					        marker = self.get_test_tags("lib", "goto")["@addFunction"]
 | 
				
			||||||
        self.open_file_and_wait_for_diagnostics(solc, 'lib', "goto")
 | 
					        self.open_file_and_wait_for_diagnostics(solc, 'lib', "goto")
 | 
				
			||||||
        solc.send_message(
 | 
					        solc.send_message(
 | 
				
			||||||
            'textDocument/didChange',
 | 
					            'textDocument/didChange',
 | 
				
			||||||
@ -1310,7 +1398,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
        report = published_diagnostics[0]
 | 
					        report = published_diagnostics[0]
 | 
				
			||||||
        self.expect_equal(report['uri'], self.get_test_file_uri('didOpen_with_import'))
 | 
					        self.expect_equal(report['uri'], self.get_test_file_uri('didOpen_with_import'))
 | 
				
			||||||
        diagnostics = report['diagnostics']
 | 
					        diagnostics = report['diagnostics']
 | 
				
			||||||
        marker = self.get_file_tags("didOpen_with_import")["@diagnostics"]
 | 
					        marker = self.get_test_tags("didOpen_with_import")["@diagnostics"]
 | 
				
			||||||
        self.expect_equal(len(diagnostics), 1, "now, no diagnostics")
 | 
					        self.expect_equal(len(diagnostics), 1, "now, no diagnostics")
 | 
				
			||||||
        self.expect_diagnostic(diagnostics[0], code=9582, marker=marker)
 | 
					        self.expect_diagnostic(diagnostics[0], code=9582, marker=marker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1343,7 +1431,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
        self.expect_equal(report['uri'], self.get_test_file_uri('lib', 'goto'), "Correct file URI")
 | 
					        self.expect_equal(report['uri'], self.get_test_file_uri('lib', 'goto'), "Correct file URI")
 | 
				
			||||||
        self.expect_equal(len(report['diagnostics']), 1, "one diagnostic")
 | 
					        self.expect_equal(len(report['diagnostics']), 1, "one diagnostic")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        markers = self.get_file_tags('lib', 'goto')
 | 
					        markers = self.get_test_tags('lib', 'goto')
 | 
				
			||||||
        marker = markers["@diagnostics"]
 | 
					        marker = markers["@diagnostics"]
 | 
				
			||||||
        self.expect_diagnostic(report['diagnostics'][0], code=2072, marker=marker)
 | 
					        self.expect_diagnostic(report['diagnostics'][0], code=2072, marker=marker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1400,7 +1488,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
        self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME, "goto"), "Correct file URI")
 | 
					        self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME, "goto"), "Correct file URI")
 | 
				
			||||||
        diagnostics = report['diagnostics']
 | 
					        diagnostics = report['diagnostics']
 | 
				
			||||||
        self.expect_equal(len(diagnostics), 3, "3 diagnostic messages")
 | 
					        self.expect_equal(len(diagnostics), 3, "3 diagnostic messages")
 | 
				
			||||||
        markers = self.get_file_tags(TEST_NAME, "goto")
 | 
					        markers = self.get_test_tags(TEST_NAME, "goto")
 | 
				
			||||||
        self.expect_diagnostic(diagnostics[0], code=6321, marker=markers["@unusedReturnVariable"])
 | 
					        self.expect_diagnostic(diagnostics[0], code=6321, marker=markers["@unusedReturnVariable"])
 | 
				
			||||||
        self.expect_diagnostic(diagnostics[1], code=2072, marker=markers["@unusedVariable"])
 | 
					        self.expect_diagnostic(diagnostics[1], code=2072, marker=markers["@unusedVariable"])
 | 
				
			||||||
        self.expect_diagnostic(diagnostics[2], code=2072, marker=markers["@unusedContractVariable"])
 | 
					        self.expect_diagnostic(diagnostics[2], code=2072, marker=markers["@unusedContractVariable"])
 | 
				
			||||||
@ -1433,7 +1521,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
        self.test_textDocument_didOpen_with_relative_import(solc)
 | 
					        self.test_textDocument_didOpen_with_relative_import(solc)
 | 
				
			||||||
        self.open_file_and_wait_for_diagnostics(solc, 'lib', 'goto')
 | 
					        self.open_file_and_wait_for_diagnostics(solc, 'lib', 'goto')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        marker = self.get_file_tags('lib', 'goto')["@diagnostics"]
 | 
					        marker = self.get_test_tags('lib', 'goto')["@diagnostics"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # lib.sol: Fix the unused variable message by removing it.
 | 
					        # lib.sol: Fix the unused variable message by removing it.
 | 
				
			||||||
        solc.send_message(
 | 
					        solc.send_message(
 | 
				
			||||||
@ -1563,7 +1651,7 @@ class SolidityLSPTestSuite: # {{{
 | 
				
			|||||||
        self.expect_equal(len(reports), 2, '')
 | 
					        self.expect_equal(len(reports), 2, '')
 | 
				
			||||||
        self.expect_equal(len(reports[0]['diagnostics']), 0, "should not contain diagnostics")
 | 
					        self.expect_equal(len(reports[0]['diagnostics']), 0, "should not contain diagnostics")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        marker = self.get_file_tags("lib", 'goto')["@diagnostics"]
 | 
					        marker = self.get_test_tags("lib", 'goto')["@diagnostics"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # unused variable in lib.sol
 | 
					        # unused variable in lib.sol
 | 
				
			||||||
        self.expect_diagnostic(reports[1]['diagnostics'][0], code=2072, marker=marker)
 | 
					        self.expect_diagnostic(reports[1]['diagnostics'][0], code=2072, marker=marker)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user