From f308f1a1f82acea226d70af16dd1ca14a75a9a6d Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 25 Apr 2022 15:35:41 +0200 Subject: [PATCH] Always allow full filesystem access to LSP. --- Changelog.md | 1 + libsolidity/CMakeLists.txt | 1 + libsolidity/lsp/FileRepository.cpp | 128 ++++++++++++++---- libsolidity/lsp/FileRepository.h | 40 ++++-- libsolidity/lsp/HandlerBase.cpp | 4 +- libsolidity/lsp/LanguageServer.cpp | 19 ++- libsolidity/lsp/LanguageServer.h | 2 +- libsolidity/lsp/Transport.cpp | 200 +++++++++++++++++++---------- libsolidity/lsp/Transport.h | 69 +++++++--- libsolidity/lsp/Utils.cpp | 13 +- libsolidity/lsp/Utils.h | 7 + solc/CommandLineInterface.cpp | 2 +- 12 files changed, 350 insertions(+), 136 deletions(-) diff --git a/Changelog.md b/Changelog.md index 01c288a2e..27ae4e30a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: * Assembly-Json: Export: Include source list in `sourceList` field. * Commandline Interface: option ``--pretty-json`` works also with the following options: ``--abi``, ``--asm-json``, ``--ast-compact-json``, ``--devdoc``, ``--storage-layout``, ``--userdoc``. * SMTChecker: Support ``abi.encodeCall`` taking into account the called selector. + * Language Server: Allow full filesystem access to language server. Bugfixes: diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 3d2845463..26c14976e 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -176,3 +176,4 @@ set(sources add_library(solidity ${sources}) target_link_libraries(solidity PUBLIC yul evmasm langutil smtutil solutil Boost::boost fmt::fmt-header-only) + diff --git a/libsolidity/lsp/FileRepository.cpp b/libsolidity/lsp/FileRepository.cpp index 9c7f72e0c..e82343973 100644 --- a/libsolidity/lsp/FileRepository.cpp +++ b/libsolidity/lsp/FileRepository.cpp @@ -17,48 +17,130 @@ // SPDX-License-Identifier: GPL-3.0 #include +#include + +#include +#include + +#include +#include + +#include using namespace std; using namespace solidity; using namespace solidity::lsp; +using namespace solidity::frontend; -namespace -{ +using solidity::util::readFileAsString; +using solidity::util::joinHumanReadable; -string stripFilePrefix(string const& _path) +FileRepository::FileRepository(boost::filesystem::path _basePath): m_basePath(std::move(_basePath)) { - if (_path.find("file://") == 0) - return _path.substr(7); - else - return _path; } -} - -string FileRepository::sourceUnitNameToClientPath(string const& _sourceUnitName) const +string FileRepository::sourceUnitNameToUri(string const& _sourceUnitName) const { - if (m_sourceUnitNamesToClientPaths.count(_sourceUnitName)) - return m_sourceUnitNamesToClientPaths.at(_sourceUnitName); + regex const windowsDriveLetterPath("^[a-zA-Z]:/"); + + if (m_sourceUnitNamesToUri.count(_sourceUnitName)) + return m_sourceUnitNamesToUri.at(_sourceUnitName); else if (_sourceUnitName.find("file://") == 0) return _sourceUnitName; + else if (regex_search(_sourceUnitName, windowsDriveLetterPath)) + return "file:///" + _sourceUnitName; + else if (_sourceUnitName.find("/") == 0) + return "file://" + _sourceUnitName; else - return "file://" + (m_fileReader.basePath() / _sourceUnitName).generic_string(); + return "file://" + m_basePath.generic_string() + "/" + _sourceUnitName; } -string FileRepository::clientPathToSourceUnitName(string const& _path) const +string FileRepository::uriToSourceUnitName(string const& _path) const { - return m_fileReader.cliPathToSourceUnitName(stripFilePrefix(_path)); + return stripFileUriSchemePrefix(_path); } -map const& FileRepository::sourceUnits() const -{ - return m_fileReader.sourceUnits(); -} - -void FileRepository::setSourceByClientPath(string const& _uri, string _text) +void FileRepository::setSourceByUri(string const& _uri, string _source) { // This is needed for uris outside the base path. It can lead to collisions, // but we need to mostly rewrite this in a future version anyway. - m_sourceUnitNamesToClientPaths.emplace(clientPathToSourceUnitName(_uri), _uri); - m_fileReader.addOrUpdateFile(stripFilePrefix(_uri), move(_text)); + auto sourceUnitName = uriToSourceUnitName(_uri); + m_sourceUnitNamesToUri.emplace(sourceUnitName, _uri); + m_sourceCodes[sourceUnitName] = std::move(_source); } + +frontend::ReadCallback::Result FileRepository::readFile(string const& _kind, string const& _sourceUnitName) +{ + solAssert( + _kind == ReadCallback::kindString(ReadCallback::Kind::ReadFile), + "ReadFile callback used as callback kind " + _kind + ); + + try + { + // File was read already. Use local store. + if (m_sourceCodes.count(_sourceUnitName)) + return ReadCallback::Result{true, m_sourceCodes.at(_sourceUnitName)}; + + string const strippedSourceUnitName = stripFileUriSchemePrefix(_sourceUnitName); + + if ( + boost::filesystem::path(strippedSourceUnitName).has_root_path() && + boost::filesystem::exists(strippedSourceUnitName) + ) + { + auto contents = readFileAsString(strippedSourceUnitName); + solAssert(m_sourceCodes.count(_sourceUnitName) == 0, ""); + m_sourceCodes[_sourceUnitName] = contents; + return ReadCallback::Result{true, move(contents)}; + } + + vector candidates; + vector> prefixes = {m_basePath}; + prefixes += (m_includePaths | ranges::to>>); + + auto const pathToQuotedString = [](boost::filesystem::path const& _path) { return "\"" + _path.string() + "\""; }; + + for (auto const& prefix: prefixes) + { + boost::filesystem::path canonicalPath = boost::filesystem::path(prefix) / boost::filesystem::path(strippedSourceUnitName); + + if (boost::filesystem::exists(canonicalPath)) + candidates.push_back(move(canonicalPath)); + } + + if (candidates.empty()) + return ReadCallback::Result{ + false, + "File not found. Searched the following locations: " + + joinHumanReadable(prefixes | ranges::views::transform(pathToQuotedString), ", ") + + "." + }; + + if (candidates.size() >= 2) + return ReadCallback::Result{ + false, + "Ambiguous import. " + "Multiple matching files found inside base path and/or include paths: " + + joinHumanReadable(candidates | ranges::views::transform(pathToQuotedString), ", ") + + "." + }; + + if (!boost::filesystem::is_regular_file(candidates[0])) + return ReadCallback::Result{false, "Not a valid file."}; + + auto contents = readFileAsString(candidates[0]); + solAssert(m_sourceCodes.count(_sourceUnitName) == 0, ""); + m_sourceCodes[_sourceUnitName] = contents; + return ReadCallback::Result{true, move(contents)}; + } + catch (std::exception const& _exception) + { + return ReadCallback::Result{false, "Exception in read callback: " + boost::diagnostic_information(_exception)}; + } + catch (...) + { + return ReadCallback::Result{false, "Unknown exception in read callback: " + boost::current_exception_diagnostic_information()}; + } +} + diff --git a/libsolidity/lsp/FileRepository.h b/libsolidity/lsp/FileRepository.h index b6aa5ee08..152c1d4be 100644 --- a/libsolidity/lsp/FileRepository.h +++ b/libsolidity/lsp/FileRepository.h @@ -28,26 +28,42 @@ namespace solidity::lsp class FileRepository { public: - explicit FileRepository(boost::filesystem::path const& _basePath): - m_fileReader(_basePath) {} + explicit FileRepository(boost::filesystem::path _basePath); - boost::filesystem::path const& basePath() const { return m_fileReader.basePath(); } + boost::filesystem::path const& basePath() const { return m_basePath; } /// Translates a compiler-internal source unit name to an LSP client path. - std::string sourceUnitNameToClientPath(std::string const& _sourceUnitName) const; - /// Translates an LSP client path into a compiler-internal source unit name. - std::string clientPathToSourceUnitName(std::string const& _uri) const; + std::string sourceUnitNameToUri(std::string const& _sourceUnitName) const; + + /// Translates an LSP file URI into a compiler-internal source unit name. + std::string uriToSourceUnitName(std::string const& _uri) const; /// @returns all sources by their compiler-internal source unit name. - std::map const& sourceUnits() const; - /// Changes the source identified by the LSP client path _uri to _text. - void setSourceByClientPath(std::string const& _uri, std::string _text); + StringMap const& sourceUnits() const noexcept { return m_sourceCodes; } - frontend::ReadCallback::Callback reader() { return m_fileReader.reader(); } + /// Changes the source identified by the LSP client path _uri to _text. + void setSourceByUri(std::string const& _uri, std::string _text); + + void addOrUpdateFile(boost::filesystem::path const& _path, frontend::SourceCode _source); + void setSourceUnits(StringMap _sources); + frontend::ReadCallback::Result readFile(std::string const& _kind, std::string const& _sourceUnitName); + frontend::ReadCallback::Callback reader() + { + return [this](std::string const& _kind, std::string const& _path) { return readFile(_kind, _path); }; + } private: - std::map m_sourceUnitNamesToClientPaths; - frontend::FileReader m_fileReader; + /// Base path without URI scheme. + boost::filesystem::path m_basePath; + + /// Additional directories used for resolving relative paths in imports. + std::vector m_includePaths; + + /// Mapping of source unit names to their URIs as understood by the client. + StringMap m_sourceUnitNamesToUri; + + /// Mapping of source unit names to their file content. + StringMap m_sourceCodes; }; } diff --git a/libsolidity/lsp/HandlerBase.cpp b/libsolidity/lsp/HandlerBase.cpp index 0da19aad3..f40f189ab 100644 --- a/libsolidity/lsp/HandlerBase.cpp +++ b/libsolidity/lsp/HandlerBase.cpp @@ -47,7 +47,7 @@ Json::Value HandlerBase::toJson(SourceLocation const& _location) const { solAssert(_location.sourceName); Json::Value item = Json::objectValue; - item["uri"] = fileRepository().sourceUnitNameToClientPath(*_location.sourceName); + item["uri"] = fileRepository().sourceUnitNameToUri(*_location.sourceName); item["range"] = toRange(_location); return item; } @@ -55,7 +55,7 @@ Json::Value HandlerBase::toJson(SourceLocation const& _location) const pair HandlerBase::extractSourceUnitNameAndLineColumn(Json::Value const& _args) const { string const uri = _args["textDocument"]["uri"].asString(); - string const sourceUnitName = fileRepository().clientPathToSourceUnitName(uri); + string const sourceUnitName = fileRepository().uriToSourceUnitName(uri); if (!fileRepository().sourceUnits().count(sourceUnitName)) BOOST_THROW_EXCEPTION( RequestError(ErrorCode::RequestFailed) << diff --git a/libsolidity/lsp/LanguageServer.cpp b/libsolidity/lsp/LanguageServer.cpp index 8a42ca104..44b0cf041 100644 --- a/libsolidity/lsp/LanguageServer.cpp +++ b/libsolidity/lsp/LanguageServer.cpp @@ -37,8 +37,6 @@ #include #include -#include - #include #include @@ -114,9 +112,9 @@ void LanguageServer::compile() swap(oldRepository, m_fileRepository); for (string const& fileName: m_openFiles) - m_fileRepository.setSourceByClientPath( + m_fileRepository.setSourceByUri( fileName, - oldRepository.sourceUnits().at(oldRepository.clientPathToSourceUnitName(fileName)) + oldRepository.sourceUnits().at(oldRepository.uriToSourceUnitName(fileName)) ); // TODO: optimize! do not recompile if nothing has changed (file(s) not flagged dirty). @@ -178,7 +176,7 @@ void LanguageServer::compileAndUpdateDiagnostics() for (auto&& [sourceUnitName, diagnostics]: diagnosticsBySourceUnit) { Json::Value params; - params["uri"] = m_fileRepository.sourceUnitNameToClientPath(sourceUnitName); + params["uri"] = m_fileRepository.sourceUnitNameToUri(sourceUnitName); if (!diagnostics.empty()) m_nonemptyDiagnostics.insert(sourceUnitName); params["diagnostics"] = move(diagnostics); @@ -252,13 +250,12 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args) ErrorCode::InvalidParams, "rootUri only supports file URI scheme." ); - - rootPath = rootPath.substr(7); + rootPath = stripFileUriSchemePrefix(rootPath); } else if (Json::Value rootPath = _args["rootPath"]) rootPath = rootPath.asString(); - m_fileRepository = FileRepository(boost::filesystem::path(rootPath)); + m_fileRepository = FileRepository(rootPath); if (_args["initializationOptions"].isObject()) changeConfiguration(_args["initializationOptions"]); @@ -309,7 +306,7 @@ void LanguageServer::handleTextDocumentDidOpen(Json::Value const& _args) string text = _args["textDocument"]["text"].asString(); string uri = _args["textDocument"]["uri"].asString(); m_openFiles.insert(uri); - m_fileRepository.setSourceByClientPath(uri, move(text)); + m_fileRepository.setSourceByUri(uri, move(text)); compileAndUpdateDiagnostics(); } @@ -327,7 +324,7 @@ void LanguageServer::handleTextDocumentDidChange(Json::Value const& _args) "Invalid content reference." ); - string const sourceUnitName = m_fileRepository.clientPathToSourceUnitName(uri); + string const sourceUnitName = m_fileRepository.uriToSourceUnitName(uri); lspAssert( m_fileRepository.sourceUnits().count(sourceUnitName), ErrorCode::RequestFailed, @@ -348,7 +345,7 @@ void LanguageServer::handleTextDocumentDidChange(Json::Value const& _args) buffer.replace(static_cast(change->start), static_cast(change->end - change->start), move(text)); text = move(buffer); } - m_fileRepository.setSourceByClientPath(uri, move(text)); + m_fileRepository.setSourceByUri(uri, move(text)); } compileAndUpdateDiagnostics(); diff --git a/libsolidity/lsp/LanguageServer.h b/libsolidity/lsp/LanguageServer.h index e827f6259..2a7951fb0 100644 --- a/libsolidity/lsp/LanguageServer.h +++ b/libsolidity/lsp/LanguageServer.h @@ -92,7 +92,7 @@ private: Transport& m_client; std::map m_handlers; - /// Set of files known to be open by the client. + /// Set of files (names in URI form) known to be open by the client. std::set m_openFiles; /// Set of source unit names for which we sent diagnostics to the client in the last iteration. std::set m_nonemptyDiagnostics; diff --git a/libsolidity/lsp/Transport.cpp b/libsolidity/lsp/Transport.cpp index b82b34ec4..aa85fd6b1 100644 --- a/libsolidity/lsp/Transport.cpp +++ b/libsolidity/lsp/Transport.cpp @@ -22,31 +22,25 @@ #include #include +#include + #include #include #include +#include + + +#if defined(_WIN32) +#include +#include +#endif using namespace std; using namespace solidity::lsp; -IOStreamTransport::IOStreamTransport(istream& _in, ostream& _out): - m_input{_in}, - m_output{_out} -{ -} - -IOStreamTransport::IOStreamTransport(): - IOStreamTransport(cin, cout) -{ -} - -bool IOStreamTransport::closed() const noexcept -{ - return m_input.eof(); -} - -optional IOStreamTransport::receive() +// {{{ Transport +optional Transport::receive() { auto const headers = parseHeaders(); if (!headers) @@ -61,7 +55,7 @@ optional IOStreamTransport::receive() return nullopt; } - string const data = util::readBytes(m_input, stoul(headers->at("content-length"))); + string const data = readBytes(stoul(headers->at("content-length"))); Json::Value jsonMessage; string jsonParsingErrors; @@ -75,29 +69,6 @@ optional IOStreamTransport::receive() return {move(jsonMessage)}; } -void IOStreamTransport::notify(string _method, Json::Value _message) -{ - Json::Value json; - json["method"] = move(_method); - json["params"] = move(_message); - send(move(json)); -} - -void IOStreamTransport::reply(MessageID _id, Json::Value _message) -{ - Json::Value json; - json["result"] = move(_message); - send(move(json), _id); -} - -void IOStreamTransport::error(MessageID _id, ErrorCode _code, string _message) -{ - Json::Value json; - json["error"]["code"] = static_cast(_code); - json["error"]["message"] = move(_message); - send(move(json), _id); -} - void Transport::trace(std::string _message, Json::Value _extra) { if (m_logTrace != TraceValue::Off) @@ -110,30 +81,13 @@ void Transport::trace(std::string _message, Json::Value _extra) } } -void IOStreamTransport::send(Json::Value _json, MessageID _id) -{ - solAssert(_json.isObject()); - _json["jsonrpc"] = "2.0"; - if (_id != Json::nullValue) - _json["id"] = _id; - - string const jsonString = solidity::util::jsonCompactPrint(_json); - - m_output << "Content-Length: " << jsonString.size() << "\r\n"; - m_output << "\r\n"; - m_output << jsonString; - - m_output.flush(); -} - -optional> IOStreamTransport::parseHeaders() +optional> Transport::parseHeaders() { map headers; while (true) { - string line; - getline(m_input, line); + auto line = getline(); if (boost::trim_copy(line).empty()) break; @@ -141,13 +95,127 @@ optional> IOStreamTransport::parseHeaders() if (delimiterPos == string::npos) return nullopt; - string name = boost::to_lower_copy(line.substr(0, delimiterPos)); - string value = line.substr(delimiterPos + 1); - if (!headers.emplace( - boost::trim_copy(name), - boost::trim_copy(value) - ).second) + auto const name = boost::to_lower_copy(line.substr(0, delimiterPos)); + auto const value = line.substr(delimiterPos + 1); + if (!headers.emplace(boost::trim_copy(name), boost::trim_copy(value)).second) return nullopt; } return {move(headers)}; } + +void Transport::notify(string _method, Json::Value _message) +{ + Json::Value json; + json["method"] = move(_method); + json["params"] = move(_message); + send(move(json)); +} + +void Transport::reply(MessageID _id, Json::Value _message) +{ + Json::Value json; + json["result"] = move(_message); + send(move(json), _id); +} + +void Transport::error(MessageID _id, ErrorCode _code, string _message) +{ + Json::Value json; + json["error"]["code"] = static_cast(_code); + json["error"]["message"] = move(_message); + send(move(json), _id); +} + +void Transport::send(Json::Value _json, MessageID _id) +{ + solAssert(_json.isObject()); + _json["jsonrpc"] = "2.0"; + if (_id != Json::nullValue) + _json["id"] = _id; + + // Trailing CRLF only for easier readability. + string const jsonString = solidity::util::jsonCompactPrint(_json); + + writeBytes(fmt::format("Content-Length: {}\r\n\r\n", jsonString.size())); + writeBytes(jsonString); + flushOutput(); +} +// }}} + +// {{{ IOStreamTransport +IOStreamTransport::IOStreamTransport(istream& _in, ostream& _out): + m_input{_in}, + m_output{_out} +{ +} + +bool IOStreamTransport::closed() const noexcept +{ + return m_input.eof(); +} + +std::string IOStreamTransport::readBytes(size_t _length) +{ + return util::readBytes(m_input, _length); +} + +std::string IOStreamTransport::getline() +{ + string line; + std::getline(m_input, line); + return line; +} + +void IOStreamTransport::writeBytes(std::string_view _data) +{ + m_output.write(_data.data(), static_cast(_data.size())); +} + +void IOStreamTransport::flushOutput() +{ + m_output.flush(); +} +// }}} + +// {{{ StdioTransport +StdioTransport::StdioTransport() +{ + #if defined(_WIN32) + // Attempt to change the modes of stdout from text to binary. + setmode(fileno(stdout), O_BINARY); + #endif +} + +bool StdioTransport::closed() const noexcept +{ + return feof(stdin); +} + +std::string StdioTransport::readBytes(size_t _byteCount) +{ + std::string buffer; + buffer.resize(_byteCount); + auto const n = fread(buffer.data(), 1, _byteCount, stdin); + if (n < _byteCount) + buffer.resize(n); + return buffer; +} + +std::string StdioTransport::getline() +{ + std::string line; + std::getline(std::cin, line); + return line; +} + +void StdioTransport::writeBytes(std::string_view _data) +{ + auto const bytesWritten = fwrite(_data.data(), 1, _data.size(), stdout); + solAssert(bytesWritten == _data.size()); +} + +void StdioTransport::flushOutput() +{ + fflush(stdout); +} +// }}} diff --git a/libsolidity/lsp/Transport.h b/libsolidity/lsp/Transport.h index d84edf49d..8aa89b3d2 100644 --- a/libsolidity/lsp/Transport.h +++ b/libsolidity/lsp/Transport.h @@ -91,20 +91,44 @@ class Transport public: virtual ~Transport() = default; + std::optional receive(); + void notify(std::string _method, Json::Value _params); + void reply(MessageID _id, Json::Value _result); + void error(MessageID _id, ErrorCode _code, std::string _message); + virtual bool closed() const noexcept = 0; - virtual std::optional receive() = 0; - virtual void notify(std::string _method, Json::Value _params) = 0; - virtual void reply(MessageID _id, Json::Value _result) = 0; - virtual void error(MessageID _id, ErrorCode _code, std::string _message) = 0; void trace(std::string _message, Json::Value _extra = Json::nullValue); TraceValue traceValue() const noexcept { return m_logTrace; } void setTrace(TraceValue _value) noexcept { m_logTrace = _value; } - private: TraceValue m_logTrace = TraceValue::Off; + +protected: + /// Reads from the transport and parses the headers until the beginning + /// of the contents. + std::optional> parseHeaders(); + + /// Consumes exactly @p _byteCount bytes, as needed for consuming + /// the message body from the transport line. + virtual std::string readBytes(size_t _byteCount) = 0; + + // Mimmicks std::getline() on this Transport API. + virtual std::string getline() = 0; + + /// Writes the given payload @p _data to transport. + /// This call may or may not buffer. + virtual void writeBytes(std::string_view _data) = 0; + + /// Ensures transport output is flushed. + virtual void flushOutput() = 0; + + /// Sends an arbitrary raw message to the client. + /// + /// Used by the notify/reply/error function family. + virtual void send(Json::Value _message, MessageID _id = Json::nullValue); }; /** @@ -119,27 +143,34 @@ public: /// @param _out for example std::cout (stdout) IOStreamTransport(std::istream& _in, std::ostream& _out); - // Constructs a JSON transport using standard I/O streams. - IOStreamTransport(); - bool closed() const noexcept override; - std::optional receive() override; - void notify(std::string _method, Json::Value _params) override; - void reply(MessageID _id, Json::Value _result) override; - void error(MessageID _id, ErrorCode _code, std::string _message) override; protected: - /// Sends an arbitrary raw message to the client. - /// - /// Used by the notify/reply/error function family. - virtual void send(Json::Value _message, MessageID _id = Json::nullValue); - - /// Parses header section from the client including message-delimiting empty line. - std::optional> parseHeaders(); + std::string readBytes(size_t _byteCount) override; + std::string getline() override; + void writeBytes(std::string_view _data) override; + void flushOutput() override; private: std::istream& m_input; std::ostream& m_output; }; +/** + * Standard I/O transport Layer utilizing stdin/stdout for communication. + */ +class StdioTransport: public Transport +{ +public: + StdioTransport(); + + bool closed() const noexcept override; + +protected: + std::string readBytes(size_t _byteCount) override; + std::string getline() override; + void writeBytes(std::string_view _data) override; + void flushOutput() override; +}; + } diff --git a/libsolidity/lsp/Utils.cpp b/libsolidity/lsp/Utils.cpp index 624d1f150..ef19b4c2e 100644 --- a/libsolidity/lsp/Utils.cpp +++ b/libsolidity/lsp/Utils.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include namespace solidity::lsp @@ -115,4 +115,15 @@ optional parseRange(FileRepository const& _fileRepository, strin return start; } +string stripFileUriSchemePrefix(string const& _path) +{ + regex const windowsDriveLetterPath("^file:///[a-zA-Z]:/"); + if (regex_search(_path, windowsDriveLetterPath)) + return _path.substr(8); + if (_path.find("file://") == 0) + return _path.substr(7); + else + return _path; +} + } diff --git a/libsolidity/lsp/Utils.h b/libsolidity/lsp/Utils.h index 3594efba2..c6d40213e 100644 --- a/libsolidity/lsp/Utils.h +++ b/libsolidity/lsp/Utils.h @@ -64,6 +64,13 @@ std::optional parseRange( Json::Value const& _range ); +/// Strips the file:// URI prefix off the given path, if present, +/// also taking special care of Windows-drive-letter paths. +/// +/// So file:///path/to/some/file.txt returns /path/to/some/file.txt, as well as, +/// file:///C:/file.txt will return C:/file.txt (forward-slash is okay on Windows). +std::string stripFileUriSchemePrefix(std::string const& _path); + /// Extracts the resolved declaration of the given expression AST node. /// /// This may for example be the type declaration of an identifier, diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 40a428c95..7e627208f 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -912,7 +912,7 @@ void CommandLineInterface::handleAst() void CommandLineInterface::serveLSP() { - lsp::IOStreamTransport transport; + lsp::StdioTransport transport; if (!lsp::LanguageServer{transport}.run()) solThrow(CommandLineExecutionError, "LSP terminated abnormally."); }