Skip to content

Reuse the latest-version parse instead of parsing twice - #1750

Open
Janther wants to merge 4 commits into
mainfrom
parser-is-not-used
Open

Janther wants to merge 4 commits into
mainfrom
parser-is-not-used

Conversation

@Janther

@Janther Janther commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Slang's inferLanguageVersions internally parses the whole file using the latest parser they have and fetches all VersionExpressionSets to then return an array of matching versions.

The way we were parsing, we were doing the work twice, once for inference and once for the actual parse.
By parsing directly with the latest parser and collecting the pragmas, we can use a much smaller input to infer the same array of versions and then if we happen to match with the latest version, we have an output ready to go.

As an example for OpenZeppelin v3.4's TimelockController.sol (301 lines), the inference receives only:

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.9 <0.8.0;

pragma experimental ABIEncoderV2;

The highest version these pragmas allow is 0.7.6, not the latest, so we parse again with 0.7.6. That's the same two parses as before, plus collecting the pragmas through calls from JavaScript into Slang instead of entirely inside Slang. That overhead is small enough to be lost in the noise here (15.2 ms → 15.1 ms).

Pragmas anywhere in the file are found, which matters for flattened files. For OpenZeppelin's ERC20 flattened with its dependencies (604 lines, 7 contracts), it receives only:

// File: …/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.16;

// File: …/token/ERC20/extensions/IERC20Metadata.sol
pragma solidity >=0.6.2;

// File: …/utils/Context.sol
pragma solidity ^0.8.20;

// File: …/interfaces/draft-IERC6093.sol
pragma solidity >=0.8.4;

// File: …/token/ERC20/ERC20.sol
pragma solidity ^0.8.20;

Here the highest allowed version is the latest one (0.8.37), so the parse we already have is the result: 1 parse instead of an inference plus a parse (12.5 ms → 6.7 ms).

In the worst case, the inferred array will force us to parse again with an older version which is roughly the same amount of work we were originally doing.

File Work Compared with main
The pragmas allow the latest version 1 parse ~30% faster
The pragmas need an older version (e.g. 0.8.26), or the latest version can't parse the file (syntax from before 0.8.0) 2 parses, plus collecting the pragmas from JavaScript about the same

createParser also now returns { version, parseOutput } instead of the Parser, since callers only ever read parser.languageVersion.

The name createParser is not accurate anymore, since we return { version, parseOutput }, so I would be changing it in a PR after this since it touches a few files in testing as well. I'm thinking about parseSourceUnit() since that describes better what it does. Welcoming suggestions @fvictorio

Benchmark

Formatting OpenZeppelin v5.7.0's contracts/ (368 files) with Prettier 3.9.9 on Node 26 (Apple M3 Pro):

hyperfine --warmup 2 --runs 10 --ignore-failure \
  -n main "npx prettier --check --no-config --plugin=./main/dist/index.js 'contracts/**/*.sol'" \
  -n pr   "npx prettier --check --no-config --plugin=./pr/dist/index.js 'contracts/**/*.sol'"
Time
main 3.110 s ± 0.020 s
This PR 2.381 s ± 0.023 s

1.31× faster.

@Janther
Janther requested a review from fvictorio October 2, 2026 21:58
@Janther Janther added the refactor reorganising the code without changes label Oct 2, 2026
@Janther Janther changed the title removing parser since we don't really do anything with it. Always parse with the latest Parser Oct 4, 2026
@Janther Janther changed the title Always parse with the latest Parser Reuse the latest-version parse instead of parsing twice Oct 4, 2026
@Janther Janther added performance Results in smaller size, better memory usage, or faster execution and removed refactor reorganising the code without changes labels Oct 4, 2026
@Janther
Janther force-pushed the parser-is-not-used branch from 8e35c09 to 35f32a1 Compare October 12, 2026 02:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Results in smaller size, better memory usage, or faster execution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant