Skip to content

Accept '$' in @param and @return names - #486

Open
pucedoteth wants to merge 1 commit into
OpenZeppelin:masterfrom
pucedoteth:fix-dollar-in-natspec-names
Open

pucedoteth wants to merge 1 commit into
OpenZeppelin:masterfrom
pucedoteth:fix-dollar-in-natspec-names

Conversation

@pucedoteth

Copy link
Copy Markdown

Fixes #464.

The bug

Solidity identifiers may contain $ ([a-zA-Z$_][a-zA-Z0-9$_]*), but both NatSpec tag parsers matched the name with \w+, which is [A-Za-z0-9_]:

const paramMatches = content.match(/(\w+) ([^]*)/);      // @param
const paramMatches = content.match(/(\w+)( ([^]*))?/);   // @return

\w+ cannot match $, so the regex skips past it and captures the next word instead. The consequences differ by tag:

tag content name parsed result
@return $ the storage struct Storage throws Expected @return tag to start with name '$'
@param $c the operand the silently wrong
@param a$b the operand b silently wrong

The @return case is the one reported in #464. The @param case is quieter and arguably worse: nothing throws, the name simply fails to match the parameter in getParams, and the description is dropped from the generated docs without any error.

This shows up in practice because ERC-7201 namespaced storage conventionally names its struct pointer $, so documenting an accessor is enough to trigger it:

/**
 * @notice Loads the storage struct from its slot.
 * @return $ the storage struct pointer
 */
function loadStorage() internal pure returns (Storage storage $) { ... }

The change

One shared pattern, used by both tags:

const IDENTIFIER = String.raw`[A-Za-z_$][A-Za-z0-9_$]*`;

_-prefixed and alphanumeric names parse exactly as before; $, $foo and a$b now parse correctly.

Tests

test-contracts/S08_Dollar.sol covers @return $, @param a$b, @param $c and @return $d, and the render test asserts that each description attaches to the right parameter — so it catches the silent @param case as well as the throw.

One small change to the test helper was needed to make this testable at all: testRender called buildSite(build, cfg) without the third properties argument, so the computed params / returns / items getters were never attached and any template touching them rendered empty. It now forwards spec.properties ?? {}, which is what main.ts does and is a no-op for the existing tests.

Reverting only src/utils/natspec.ts and keeping the tests:

✘ [fail]: render › dollar in parameter and return names Error thrown in test
  Error (ItemError) {
    message: 'Expected @return tag to start with name \'$\' (test-contracts/S08_Dollar.sol:14)',

With the fix, npm test is 9 tests passed, and tsc --noEmit is clean.

AI disclosure

Written with AI assistance (Claude Code): the investigation, the fix, the tests, and this description.

🤖 Generated with Claude Code

Solidity identifiers may contain '$', but both NatSpec tag parsers matched
the name with \w+, which does not. ERC-7201 namespaced storage conventionally
names its struct pointer '$', so `@return $ ...` raised "Expected @return tag
to start with name '$'", and `@param a$b ...` silently parsed the name as 'b'
and dropped the description.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

using $ as a return parameter name doesn't work

1 participant