Skip to main content

Introducing ink! Analyzer

· 7 min read
David Semakula

ink! Analyzer cover image

I am proud to announce the successful completion of the Web3 Foundation grant for developing ink! analyzer - a collection of modular and reusable libraries and tools for semantic analysis of ink! smart contract code.

ink! analyzer aims to improve ink! language support in integrated development environments (IDEs), source code editors and other development tools by providing modular and reusable building blocks for implementing language features (e.g. diagnostic errors, quick fixes, code completion suggestions, code/intent actions and hover content e.t.c) for the ink! programming language which is used for writing smart contracts for blockchains built with Substrate.

Problem

ink! is an Embedded Domain Specific Language (eDSL) that you can use to write WebAssembly based smart contracts in the Rust programming language. In fact, "ink! is just standard Rust in a well-defined "contract format" with specialized #[ink(…)] attribute macros".

This allows ink! developers to leverage Rust tooling like clippy, cargo, crates.io and excellent IDE/code editor support via rust-analyzer and IntelliJ Rust.

However, relying on only generic Rust language support in IDEs, code editors and other development tools has some significant limitations for the developer experience including:

Solution

To solve the above challenges and improve ink! language support in IDEs, code editors and other development tools, ink! analyzer creates two main components:

These two components can be reused to add ink! language support to multiple IDEs, code editors and other development tools.

In particular, a large number of IDEs and code editors support LSP servers either via configurable LSP clients or robust LSP client libraries/APIs/modules, including Visual Studio Code, Visual Studio, Vim / Neovim, Emacs, Atom, Sublime Text, Acme, Lapce, Eclipse and many more.

ink! analyzer makes it relatively easy for:

  • Users to enable ink! language support for their IDE, code editor or other development tool if it has either a native/built-in or third-party LSP client that can be configured to launch an LSP server using an executable command (i.e. the path to an installed ink! Language Server binary) and can use stdio (standard in/standard out) as the message transport.
  • Developers to either build extensions/plugins/integrations that add ink! language support to any tool with robust LSP client libraries/APIs/modules, or add first-class ink! language support to an existing LSP client (e.g. an open-source extension/plugin/integration).

The latter option typically provides a better user experience as the user doesn't have to manually install (and update) the ink! Language Server as it can be bundled (or installed/updated) by the extension/plugin/integration.

Therefore, in addition to distributing compiled ink! Language Server (ink-lsp-server) binaries for most of the major platforms/architectures, ink! analyzer additionally distributes a Visual Studio Code extension that ships with a bundled ink! Language Server as a showcase and reference implementation for the latter use case.

Architecture

ink! Analyzer - LSP based Architecture Diagram Figure 1: ink! Analyzer - LSP based Architecture Diagram

1. Semantic Analyzer

The semantic analyzer is responsible for parsing the smart contract code, analyzing it based on ink!'s semantic rules for smart contracts and returning semantic information for other components to consume.

It is written in Rust and uses rust-analyzer's ra_ap_syntax crate for generating the syntax tree of the smart contract code that it analyzes.

It uses ra_ap_syntax instead of other Rust parsing and syntax tree libraries because ink! analyzer has similar design goals to rust-analyzer. The most important being that parsing should be:

  • resilient (even if the input is invalid, parser tries to see as much syntax tree fragments in the input as it can).
  • lossless (even if the input is invalid, the tree produced by the parser represents it exactly).

As a concrete example, while ink!'s ink_ir crate includes syntax tree and intermediate representation (IR) modules, it uses syn as its parser. Syn however, (being "geared toward use in Rust procedural macros") assumes "that all input to it is well-formed". So for the ink!/Rust code snippet below that contains only one invalid line, syn simply returns an error while ra_ap_syntax still generates a lossless syntax tree with errors represented by "error nodes", thus allowing analysis to still be performed on the rest of the valid code.

#[ink::contract]
mod flipper {

#[ink(storage)]
pub struct Flipper {
value: bool,
}

bad statement; // This is an invalid line
}

This makes syn (and by extension ink!'s ink_ir crate) unsuitable for our IDE/code editor use case.

2. Language Server

The language server implements the Language Server Protocol (LSP) and acts as a backend that provides language support features like diagnostic errors, code completion suggestions, code/intent actions and hover content to IDEs, code editors and other development tools. It uses the semantic analyzer as the engine for providing ink! language support features by:

  • translating LSP requests into semantic analyzer interface calls.
  • translating semantic analysis results into corresponding LSP types.

It can be reused by multiple IDEs, code editors and other development tools that support LSP servers either via native/built-in LSP clients or third-party LSP client extensions/plugins/integrations.

It is written in Rust and uses:

  • rust-analyzer's lsp-server crate to handle LSP protocol handshaking and parsing messages
  • the lsp-types crate for LSP type definitions.

3. Extension/Plugin/Integration

Extensions/plugins/integrations add ink! language support to a specific IDE, code editor or development tool by communicating with the language server running as a separate process using the Language Server Protocol via JSON-RPC.

Only a VS Code extension is currently implemented. The VS Code extension is written in TypeScript using VS Code's extension API and language server client library.

Diving Deeper

You can learn more about the above components as well as other lower-level crates/modules used by ink! analyzer, including: installation, development, usage and testing instructions, library documentation, low-level technical and architectural descriptions, and access the source code using the following resources:

Conclusion

ink! analyzer is only at the very beginning of its development, so issues, bug reports, PRs and feature requests are welcome at the aforementioned GitHub repositories 🙂.

Special thanks to the Web3 Foundation for funding ink! analyzer's development via a generous grant.