solidity-language-server 0.1.33

A fast Solidity language server powered by solc and Foundry
Documentation
# Hover

## What this page covers

This page describes the current `textDocument/hover` implementation:

- how cursor position resolves to a declaration,
- how signature, selector, gas, and docs are assembled,
- how `@inheritdoc` and call-site `@param` docs are resolved,
- what is covered by tests today.

## Terms used in this page

- **`CachedBuild`**: build-time snapshot (from compiler output on disk) stored as hash maps for fast lookups.
- **`decl_index`**: `HashMap<node_id, DeclNode>` with typed declaration nodes used for O(1) declaration lookup.
- **`doc_index`**: `HashMap<DocKey, DocEntry>` built from `userdoc` + `devdoc` in compiler output.
- **`hint_index`**: prebuilt call-site index used to map a call argument back to `(decl_id, param_name)`.
- **selector**: 4-byte function/error selector or 32-byte event topic shown in hover when available.

## Why hover is built this way

Hover needs to work for multiple cases with one response:

- declaration hover (signature + docs),
- parameter hover (show matching `@param` text),
- argument-at-callsite hover (show docs for the parameter at that call position),
- Yul-linked declarations.

A single source is not enough for all of these. The implementation combines typed AST declarations (`decl_index`), doc metadata (`doc_index`), and call-site mapping (`hint_index`).

## Runtime flow

In `src/lsp.rs`, hover is a thin wrapper around:

- `hover::hover_info(&cached_build, &uri, position, &source_bytes)`

Inside `hover_info`, the flow is:

```mermaid
flowchart TD
  A["hover request"] --> B["resolve byte position"]
  B --> C["resolve node id (Yul external refs first, else AST span)"]
  C --> D["follow referencedDeclaration to declaration id"]
  D --> E["lookup typed declaration in decl_index"]
  E --> F["build markdown parts"]
  F --> G["signature / type fallback"]
  F --> H["selector"]
  F --> H2["NodeId (always)"]
  F --> I["optional gas block (sentinel gated)"]
  F --> J["docs from doc_index or NatSpec fallback"]
  F --> K["call-site @param doc via hint_index"]
  G --> Z["return Hover markdown"]
  H --> Z
  H2 --> Z
  I --> Z
  J --> Z
  K --> Z
```

## Cursor to declaration

Resolution order is:

- Try Yul bridge first using `externalReferences`.
- Fallback to AST span match using smallest containing node in the current file.
- Resolve final declaration id by following `referencedDeclaration` when present.

This ensures hover works for both high-level Solidity and inline assembly references.

## Signature and selector

For the resolved typed declaration (`DeclNode`):

- Signature is built with `build_signature()`.
- If no signature is available, hover falls back to `typeString + name`.
- Selector is shown when present:
  - function/public variable/error: 4-byte selector,
  - event: 32-byte topic hash.

## Documentation source priority

Documentation resolution fires exactly one of three branches, tried in order:

1. **`lookup_doc_entry_typed`** — look up the declaration in `doc_index` (compiler-derived `userdoc`/`devdoc`). Preferred because it contains structured docs and compiler-resolved inherited docs.
2. **`extract_doc_text` + NatSpec fallback** — if no `doc_index` entry, extract raw NatSpec from the declaration node. If the text contains `@inheritdoc`, attempt to resolve parent docs.
3. **`lookup_param_doc_typed`** — if neither of the above produces content, and the hover target is a parameter or return variable, look up the `@param`/`@return` description from the parent declaration's doc entry.

Only one of these three branches fires per hover response. The call-site `@param` detail (below) is additive and independent of this chain.

## `@inheritdoc` behavior

If raw doc text contains `@inheritdoc`, the implementation can resolve parent docs by:

- parsing parent name,
- reading the declaration selector,
- finding the parent in base contracts,
- matching by selector in the parent contract declarations.

When resolution succeeds, hover renders resolved content instead of raw `@inheritdoc` lines.

## Call-site parameter doc (argument hover)

Hover also adds parameter doc when the cursor is on an argument at a call site:

- Parse the live buffer and find enclosing call context + argument index.
- Resolve callsite semantics through the prebuilt call index.
- Resolve parameter identity `(decl_id, param_name)`.
- Fetch matching `@param` text from doc index first, then NatSpec fallback.

This allows hover to show meaningful parameter descriptions directly at usage sites.

## Output shape

Hover markdown is assembled from parts in this order:

- Signature block (or type fallback).
- Selector line when available.
- NodeId line for debugging (always shown).
- Documentation section from doc index or NatSpec fallback.
- Optional call-site `@param` detail when applicable.

Parts are separated with blank lines for readability.

## NodeId in hover

Hover always includes a `NodeId:` line showing the AST node ID for debugging purposes. Two formats:

- **Declaration hover:** ``NodeId: `2941` `` — shows the node's own ID.
- **Reference hover:** ``NodeId: `569` referencedDeclaration: `2941` `` — shows the cursor-hit node's ID and the declaration it resolves to.

This is useful for debugging cross-file resolution, verifying that PathInterner canonical IDs are correct, and understanding which AST node the server resolved from a cursor position.

## Test coverage and confidence

The hover module has broad direct tests in `src/hover.rs`:

- selector extraction (`function`, `error`, `event`, public variable, no-selector internal),
- `@inheritdoc` resolution and formatting behavior,
- `DocIndex` construction from real fixture (`poolmanager.json`),
- known selector lookup coverage (including overload cases),
- parameter doc extraction behavior.

This gives good confidence for:

- selector correctness,
- docs lookup priority,
- inherited-doc resolution,
- markdown formatting behavior.

### Recommended explicit additions

Useful end-to-end additions through LSP boundary:

- a request-level hover test verifying full assembled markdown ordering,
- a dedicated call-site argument hover test through `lsp.rs` (not just helper-level),
- a gas-sentinel hover test that checks both enabled and disabled paths through request flow.