From 8d252fe39ca1f3bbd81983e33af1d1b346975b00 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 16 Jun 2025 19:00:49 +0100 Subject: [PATCH 01/12] updating dependencies --- package-lock.json | 8 ++++---- package.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4f05f07c5..7454a338d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,14 +16,14 @@ "devDependencies": { "@babel/code-frame": "^7.27.1", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.28.0", + "@eslint/js": "^9.29.0", "@types/jest": "^29.5.14", "@types/semver": "^7.7.0", - "@typescript-eslint/eslint-plugin": "^8.34.0", - "@typescript-eslint/parser": "^8.34.0", + "@typescript-eslint/eslint-plugin": "^8.34.1", + "@typescript-eslint/parser": "^8.34.1", "c8": "^10.1.3", "cross-env": "^7.0.3", - "eslint": "^9.28.0", + "eslint": "^9.29.0", "eslint-config-prettier": "^10.1.5", "esm-utils": "^4.4.2", "globals": "^16.2.0", diff --git a/package.json b/package.json index f530043a3..0d46d8cd9 100644 --- a/package.json +++ b/package.json @@ -85,14 +85,14 @@ "devDependencies": { "@babel/code-frame": "^7.27.1", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.28.0", + "@eslint/js": "^9.29.0", "@types/jest": "^29.5.14", "@types/semver": "^7.7.0", - "@typescript-eslint/eslint-plugin": "^8.34.0", - "@typescript-eslint/parser": "^8.34.0", + "@typescript-eslint/eslint-plugin": "^8.34.1", + "@typescript-eslint/parser": "^8.34.1", "c8": "^10.1.3", "cross-env": "^7.0.3", - "eslint": "^9.28.0", + "eslint": "^9.29.0", "eslint-config-prettier": "^10.1.5", "esm-utils": "^4.4.2", "globals": "^16.2.0", From e111b95ea63c6919f141598cde051c0698a0a8e5 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 2 Jun 2025 20:01:26 +0200 Subject: [PATCH 02/12] Using Slang's language inference tool --- src/slang-utils/create-parser.ts | 168 ++++++------------- tests/unit/slang-utils/create-parser.test.js | 45 ++--- 2 files changed, 67 insertions(+), 146 deletions(-) diff --git a/src/slang-utils/create-parser.ts b/src/slang-utils/create-parser.ts index 60bfb5dd5..ed0979d9b 100644 --- a/src/slang-utils/create-parser.ts +++ b/src/slang-utils/create-parser.ts @@ -1,16 +1,7 @@ -import { VersionExpressionSets as SlangVersionExpressionSets } from '@nomicfoundation/slang/ast'; -import { NonterminalKind, Query } from '@nomicfoundation/slang/cst'; +import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { Parser } from '@nomicfoundation/slang/parser'; import { LanguageFacts } from '@nomicfoundation/slang/utils'; -import { - maxSatisfying, - minSatisfying, - minor, - major, - satisfies, - validRange -} from 'semver'; -import { VersionExpressionSets } from '../slang-nodes/VersionExpressionSets.js'; +import { maxSatisfying } from 'semver'; import type { ParseOutput } from '@nomicfoundation/slang/parser'; import type { ParserOptions } from 'prettier'; @@ -18,128 +9,71 @@ import type { AstNode } from '../slang-nodes/types.js'; const supportedVersions = LanguageFacts.allVersions(); -const milestoneVersions = Array.from( - supportedVersions.reduce((minorRanges, version) => { - minorRanges.add(`^${major(version)}.${minor(version)}.0`); - return minorRanges; - }, new Set()) -) - .reverse() - .reduce((versions: string[], range) => { - versions.push(maxSatisfying(supportedVersions, range)!); - versions.push(minSatisfying(supportedVersions, range)!); - return versions; - }, []); - -const queries = [ - Query.create('[VersionPragma @versionRanges [VersionExpressionSets]]') -]; - -let parser: Parser; +// This list was retrieved from Slang's documentation. +// https://nomicfoundation.github.io/slang/latest/solidity-grammar/supported-versions/ +// TODO: use the run-time functionality if NomicFoundation decides to expose +// this information directly. +const milestoneVersions = [ + '0.4.14', + '0.4.16', + '0.4.21', + '0.4.22', + '0.4.25', + '0.5.0', + '0.5.3', + '0.5.5', + '0.5.8', + '0.5.10', + '0.5.14', + '0.6.0', + '0.6.2', + '0.6.5', + '0.6.7', + '0.6.8', + '0.6.11', + '0.7.0', + '0.7.1', + '0.7.4', + '0.8.0', + '0.8.4', + '0.8.8', + '0.8.13', + '0.8.18', + '0.8.19', + '0.8.22', + '0.8.27', + '0.8.29' +].map((milestone) => maxSatisfying(supportedVersions, `<${milestone}`)!); export function createParser( text: string, options: ParserOptions ): [Parser, ParseOutput] { + let parser: Parser; const compiler = maxSatisfying(supportedVersions, options.compiler); if (compiler) { - if (!parser || parser.languageVersion !== compiler) { - parser = Parser.create(compiler); - } + parser = Parser.create(compiler); return [parser, parser.parseNonterminal(NonterminalKind.SourceUnit, text)]; } - let isCachedParser = false; - if (parser) { - isCachedParser = true; - } else { - parser = Parser.create(milestoneVersions[0]); - } - let parseOutput; - let inferredRanges: string[] = []; - try { - parseOutput = parser.parseNonterminal(NonterminalKind.SourceUnit, text); - inferredRanges = tryToCollectPragmas(parseOutput, parser, isCachedParser); - } catch { - for ( - let i = isCachedParser ? 0 : 1; - i <= milestoneVersions.length; - i += 1 - ) { - try { - const version = milestoneVersions[i]; - parser = Parser.create(version); - parseOutput = parser.parseNonterminal(NonterminalKind.SourceUnit, text); - inferredRanges = tryToCollectPragmas(parseOutput, parser); - break; - } catch { - continue; - } - } - } + const inferredRanges: string[] = LanguageFacts.inferLanguageVersions(text); + parser = Parser.create(inferredRanges[inferredRanges.length - 1]); + parseOutput = parser.parseNonterminal(NonterminalKind.SourceUnit, text); - const satisfyingVersions = inferredRanges.reduce( - (versions, inferredRange) => { - if (!validRange(inferredRange)) { - throw new Error( - `Couldn't infer any version from the ranges in the pragmas${options.filepath ? ` for file ${options.filepath}` : ''}` - ); - } - return versions.filter((supportedVersion) => - satisfies(supportedVersion, inferredRange) - ); - }, - supportedVersions - ); + if (parseOutput.isValid()) return [parser, parseOutput]; - const inferredVersion = - satisfyingVersions.length > 0 - ? satisfyingVersions[satisfyingVersions.length - 1] - : supportedVersions[supportedVersions.length - 1]; + const inferredMilestones = milestoneVersions.filter((milestone) => + inferredRanges.includes(milestone) + ); - if (inferredVersion !== parser.languageVersion) { - parser = Parser.create(inferredVersion); + for (let i = inferredMilestones.length - 1; i > 0; i -= 1) { + const version = inferredMilestones[i]; + parser = Parser.create(version); parseOutput = parser.parseNonterminal(NonterminalKind.SourceUnit, text); + if (parseOutput.isValid()) break; } - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - return [parser, parseOutput!]; -} - -function tryToCollectPragmas( - parseOutput: ParseOutput, - parser: Parser, - isCachedParser = false -): string[] { - const matches = parseOutput.createTreeCursor().query(queries); - const ranges: string[] = []; - - let match; - while ((match = matches.next())) { - const versionRange = new SlangVersionExpressionSets( - match.captures.versionRanges[0].node.asNonterminalNode()! - ); - ranges.push( - // Replace all comments that could be in the expression with whitespace - new VersionExpressionSets(versionRange).comments.reduce( - (range, comment) => range.replace(comment.value, ' '), - versionRange.cst.unparse() - ) - ); - } - - if (ranges.length === 0) { - // If we didn't find pragmas but succeeded parsing the source we keep it. - if (!isCachedParser && parseOutput.isValid()) { - return [parser.languageVersion]; - } - // Otherwise we throw. - throw new Error( - `Using version ${parser.languageVersion} did not find any pragma statement and does not parse without errors.` - ); - } - - return ranges; + return [parser, parseOutput]; } diff --git a/tests/unit/slang-utils/create-parser.test.js b/tests/unit/slang-utils/create-parser.test.js index 3d926d6e1..71ebdf5f0 100644 --- a/tests/unit/slang-utils/create-parser.test.js +++ b/tests/unit/slang-utils/create-parser.test.js @@ -19,13 +19,25 @@ describe('inferLanguage', function () { { description: 'With nightly commit', source: `pragma solidity 0.8.18-ci.2023.1.17+commit.e7b959af;`, - version: '0.8.18' + version: '0.8.18', + // TODO: unskip this test when addresses this issue + // https://github.com/NomicFoundation/slang/issues/1346 + skip: true }, { description: 'Caret range and pinned version', source: `pragma solidity ^0.8.0; pragma solidity 0.8.2;`, version: '0.8.2' }, + { + description: 'pragma is broken by new lines, whitespace and comments', + source: `pragma solidity 0. + // comment 1 + 7. + /* comment 2*/ + 3;`, + version: '0.7.3' + }, { description: 'With multiline comment before the range', source: `pragma solidity /* comment */ 0.8.2;`, @@ -76,8 +88,8 @@ describe('inferLanguage', function () { } ]; - for (const { description, source, version } of fixtures) { - test(description, function () { + for (const { description, source, version, skip } of fixtures) { + (skip ? test.skip : test)(description, function () { const [parser] = createParser(source, options); expect(parser.languageVersion).toEqual(version); }); @@ -113,32 +125,7 @@ describe('inferLanguage', function () { expect(parser.languageVersion).toEqual(latestSupportedVersion); }); - test('should throw when a pragma is broken by new lines, whitespace and comments', function () { - expect(() => - createParser( - `pragma solidity 0. - // comment 1 - 7. - /* comment 2*/ - 3;`, - options - ) - ).toThrow( - "Couldn't infer any version from the ranges in the pragmas for file test.sol" - ); - expect(() => - createParser( - `pragma solidity 0. - // comment 1 - 7. - /* comment 2*/ - 3;`, - {} - ) - ).toThrow("Couldn't infer any version from the ranges in the pragmas"); - }); - - test.skip('should throw an error if there are incompatible ranges', function () { + test('should throw an error if there are incompatible ranges', function () { expect(() => createParser(`pragma solidity ^0.8.0; pragma solidity 0.7.6;`, options) ).toThrow(); From a1acee2dcc4d7b615110c7fc2887daf94cb98f4c Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 2 Jun 2025 20:45:58 +0200 Subject: [PATCH 03/12] Fixing test with conflicting pragmas and syntax --- tests/format/AllSolidityFeatures/AllSolidityFeatures.sol | 4 ---- .../AllSolidityFeatures/__snapshots__/format.test.js.snap | 8 -------- tests/format/Comments/Comments.sol | 3 --- tests/format/Comments/__snapshots__/format.test.js.snap | 5 ----- tests/format/Pragma/__snapshots__/format.test.js.snap | 3 ++- tests/format/Pragma/format.test.js | 2 +- 6 files changed, 3 insertions(+), 22 deletions(-) diff --git a/tests/format/AllSolidityFeatures/AllSolidityFeatures.sol b/tests/format/AllSolidityFeatures/AllSolidityFeatures.sol index 487df3a6e..081663de2 100644 --- a/tests/format/AllSolidityFeatures/AllSolidityFeatures.sol +++ b/tests/format/AllSolidityFeatures/AllSolidityFeatures.sol @@ -1,8 +1,4 @@ // Examples taken from the Solidity documentation online. - -// for pragma version numbers, see https://docs.npmjs.com/misc/semver#versions -pragma solidity 0.4.0; -pragma solidity ^0.4.0; pragma experimental ABIEncoderV2; import "SomeFile.sol"; diff --git a/tests/format/AllSolidityFeatures/__snapshots__/format.test.js.snap b/tests/format/AllSolidityFeatures/__snapshots__/format.test.js.snap index c01769dab..8551620d5 100644 --- a/tests/format/AllSolidityFeatures/__snapshots__/format.test.js.snap +++ b/tests/format/AllSolidityFeatures/__snapshots__/format.test.js.snap @@ -7,10 +7,6 @@ printWidth: 80 | printWidth =====================================input====================================== // Examples taken from the Solidity documentation online. - -// for pragma version numbers, see https://docs.npmjs.com/misc/semver#versions -pragma solidity 0.4.0; -pragma solidity ^0.4.0; pragma experimental ABIEncoderV2; import "SomeFile.sol"; @@ -370,10 +366,6 @@ using { add as + , sub } for Fixed18 global ; =====================================output===================================== // Examples taken from the Solidity documentation online. - -// for pragma version numbers, see https://docs.npmjs.com/misc/semver#versions -pragma solidity 0.4.0; -pragma solidity ^0.4.0; pragma experimental ABIEncoderV2; import "SomeFile.sol"; diff --git a/tests/format/Comments/Comments.sol b/tests/format/Comments/Comments.sol index 4167e90bc..e59f5e034 100644 --- a/tests/format/Comments/Comments.sol +++ b/tests/format/Comments/Comments.sol @@ -1,6 +1,3 @@ -pragma solidity ^0.4.29; - - contract Comments1 { /* solhint-disable var-name-mixedcase */ IEIP712DomainSeparator private EIP712domainSeparator; diff --git a/tests/format/Comments/__snapshots__/format.test.js.snap b/tests/format/Comments/__snapshots__/format.test.js.snap index 06902e3e8..1e9252a79 100644 --- a/tests/format/Comments/__snapshots__/format.test.js.snap +++ b/tests/format/Comments/__snapshots__/format.test.js.snap @@ -6,9 +6,6 @@ parsers: ["slang"] printWidth: 80 | printWidth =====================================input====================================== -pragma solidity ^0.4.29; - - contract Comments1 { /* solhint-disable var-name-mixedcase */ IEIP712DomainSeparator private EIP712domainSeparator; @@ -178,8 +175,6 @@ contract Comments13 { } } =====================================output===================================== -pragma solidity ^0.4.29; - contract Comments1 { /* solhint-disable var-name-mixedcase */ IEIP712DomainSeparator private EIP712domainSeparator; diff --git a/tests/format/Pragma/__snapshots__/format.test.js.snap b/tests/format/Pragma/__snapshots__/format.test.js.snap index 1c005d1ce..e613b05f8 100644 --- a/tests/format/Pragma/__snapshots__/format.test.js.snap +++ b/tests/format/Pragma/__snapshots__/format.test.js.snap @@ -1,7 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Pragma.sol format 1`] = ` +exports[`Pragma.sol - {"compiler":"0.8.0"} format 1`] = ` ====================================options===================================== +compiler: "0.8.0" parsers: ["slang"] printWidth: 80 | printWidth diff --git a/tests/format/Pragma/format.test.js b/tests/format/Pragma/format.test.js index 6021bbb0d..875eca87d 100644 --- a/tests/format/Pragma/format.test.js +++ b/tests/format/Pragma/format.test.js @@ -1 +1 @@ -runFormatTest(import.meta, ['slang']); +runFormatTest(import.meta, ['slang'], { compiler: '0.8.0' }); From 34ded21b0e7af82f4b6409226e4b82a90ea0b81c Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 3 Jun 2025 10:02:38 +0200 Subject: [PATCH 04/12] returning an object instead of an array for clarity --- src/slang-utils/create-parser.ts | 32 ++++++++++++-------- src/slangSolidityParser.ts | 2 +- tests/config/run-format-test.js | 2 +- tests/unit/slang-utils/create-parser.test.js | 20 +++++------- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/slang-utils/create-parser.ts b/src/slang-utils/create-parser.ts index ed0979d9b..b4caf3f8c 100644 --- a/src/slang-utils/create-parser.ts +++ b/src/slang-utils/create-parser.ts @@ -48,21 +48,24 @@ const milestoneVersions = [ export function createParser( text: string, options: ParserOptions -): [Parser, ParseOutput] { - let parser: Parser; +): { parser: Parser; parseOutput: ParseOutput } { const compiler = maxSatisfying(supportedVersions, options.compiler); if (compiler) { - parser = Parser.create(compiler); - return [parser, parser.parseNonterminal(NonterminalKind.SourceUnit, text)]; + const parser = Parser.create(compiler); + return { + parser, + parseOutput: parser.parseNonterminal(NonterminalKind.SourceUnit, text) + }; } - let parseOutput; - const inferredRanges: string[] = LanguageFacts.inferLanguageVersions(text); - parser = Parser.create(inferredRanges[inferredRanges.length - 1]); - parseOutput = parser.parseNonterminal(NonterminalKind.SourceUnit, text); + const parser = Parser.create(inferredRanges[inferredRanges.length - 1]); + const result = { + parser, + parseOutput: parser.parseNonterminal(NonterminalKind.SourceUnit, text) + }; - if (parseOutput.isValid()) return [parser, parseOutput]; + if (result.parseOutput.isValid()) return result; const inferredMilestones = milestoneVersions.filter((milestone) => inferredRanges.includes(milestone) @@ -70,10 +73,13 @@ export function createParser( for (let i = inferredMilestones.length - 1; i > 0; i -= 1) { const version = inferredMilestones[i]; - parser = Parser.create(version); - parseOutput = parser.parseNonterminal(NonterminalKind.SourceUnit, text); - if (parseOutput.isValid()) break; + result.parser = Parser.create(version); + result.parseOutput = result.parser.parseNonterminal( + NonterminalKind.SourceUnit, + text + ); + if (result.parseOutput.isValid()) break; } - return [parser, parseOutput]; + return result; } diff --git a/src/slangSolidityParser.ts b/src/slangSolidityParser.ts index 766ee6a57..43c66f9af 100644 --- a/src/slangSolidityParser.ts +++ b/src/slangSolidityParser.ts @@ -11,7 +11,7 @@ export default function parse( text: string, options: ParserOptions ): AstNode { - const [parser, parseOutput] = createParser(text, options); + const { parser, parseOutput } = createParser(text, options); if (parseOutput.isValid()) { // We update the compiler version by the inferred one. diff --git a/tests/config/run-format-test.js b/tests/config/run-format-test.js index dd1ee45d7..284528efa 100644 --- a/tests/config/run-format-test.js +++ b/tests/config/run-format-test.js @@ -334,7 +334,7 @@ async function runTest({ // same for ANTLR unless it was already given as an option. compiler: formatOptions.compiler || - createParser(code, formatOptions)[0].languageVersion, + createParser(code, formatOptions).parser.languageVersion, parser: "antlr", plugins: await getPlugins(), }); diff --git a/tests/unit/slang-utils/create-parser.test.js b/tests/unit/slang-utils/create-parser.test.js index 71ebdf5f0..d67349d19 100644 --- a/tests/unit/slang-utils/create-parser.test.js +++ b/tests/unit/slang-utils/create-parser.test.js @@ -90,38 +90,34 @@ describe('inferLanguage', function () { for (const { description, source, version, skip } of fixtures) { (skip ? test.skip : test)(description, function () { - const [parser] = createParser(source, options); + const { parser } = createParser(source, options); expect(parser.languageVersion).toEqual(version); }); } test('should use the latest successful version if the source has no pragmas', function () { - // This is to create in memory the latest parser and review the behaviour - createParser(`pragma solidity ${latestSupportedVersion};`, options); - let [parser] = createParser(`contract Foo {}`, options); + let { parser } = createParser(`contract Foo {}`, options); expect(parser.languageVersion).toEqual(latestSupportedVersion); - // This is to create in memory an old parser and review the behaviour - createParser(`pragma solidity 0.8.2;`, options); - [parser] = createParser(`contract Foo {}`, options); + ({ parser } = createParser(`contract Foo {}`, options)); expect(parser.languageVersion).toEqual(latestSupportedVersion); - [parser] = createParser(`contract Foo {byte bar;}`, options); + ({ parser } = createParser(`contract Foo {byte bar;}`, options)); expect(parser.languageVersion).toEqual('0.7.6'); }); test('should use compiler option if given', function () { - let [parser] = createParser(`pragma solidity ^0.8.0;`, { + let { parser } = createParser(`pragma solidity ^0.8.0;`, { compiler: '0.8.20' }); expect(parser.languageVersion).toEqual('0.8.20'); - [parser] = createParser(`pragma solidity ^0.8.0;`, { + ({ parser } = createParser(`pragma solidity ^0.8.0;`, { compiler: '0.8.2' - }); + })); expect(parser.languageVersion).toEqual('0.8.2'); - [parser] = createParser(`pragma solidity ^0.8.0;`, {}); + ({ parser } = createParser(`pragma solidity ^0.8.0;`, {})); expect(parser.languageVersion).toEqual(latestSupportedVersion); }); From 748e39ab14fab2b0329edd6260dd47a6d39ff65f Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 3 Jun 2025 10:15:18 +0200 Subject: [PATCH 05/12] adding some documentation and fixing a descending for loop condition --- src/slang-utils/create-parser.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/slang-utils/create-parser.ts b/src/slang-utils/create-parser.ts index b4caf3f8c..6f0042c75 100644 --- a/src/slang-utils/create-parser.ts +++ b/src/slang-utils/create-parser.ts @@ -43,7 +43,15 @@ const milestoneVersions = [ '0.8.22', '0.8.27', '0.8.29' -].map((milestone) => maxSatisfying(supportedVersions, `<${milestone}`)!); +].map( + // Since we are aiming to find the highest compatible Language version, we + // define a milestone as the highest possible supported version that is + // smaller than the original milestone taken from the documentation. + // This has an extra cost in execution time and the actual list could be + // hardcoded, but the calculation is done only once and it's easier to + // maintain. + (milestone) => maxSatisfying(supportedVersions, `<${milestone}`)! +); export function createParser( text: string, @@ -71,7 +79,7 @@ export function createParser( inferredRanges.includes(milestone) ); - for (let i = inferredMilestones.length - 1; i > 0; i -= 1) { + for (let i = inferredMilestones.length - 1; i >= 0; i -= 1) { const version = inferredMilestones[i]; result.parser = Parser.create(version); result.parseOutput = result.parser.parseNonterminal( From f7cb638f63eb357f4bb38264157fdeb290e55ac5 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 4 Jun 2025 01:34:00 +0200 Subject: [PATCH 06/12] removing unnecessary constant --- src/slang-utils/create-parser.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/slang-utils/create-parser.ts b/src/slang-utils/create-parser.ts index 6f0042c75..12dfcd723 100644 --- a/src/slang-utils/create-parser.ts +++ b/src/slang-utils/create-parser.ts @@ -80,8 +80,7 @@ export function createParser( ); for (let i = inferredMilestones.length - 1; i >= 0; i -= 1) { - const version = inferredMilestones[i]; - result.parser = Parser.create(version); + result.parser = Parser.create(inferredMilestones[i]); result.parseOutput = result.parser.parseNonterminal( NonterminalKind.SourceUnit, text From b7273576624e58061a75ac8581aa403e16f92ddc Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 5 Jun 2025 11:52:38 +0200 Subject: [PATCH 07/12] small refactor for DRY code --- src/slang-utils/create-parser.ts | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/slang-utils/create-parser.ts b/src/slang-utils/create-parser.ts index 12dfcd723..06088dfe6 100644 --- a/src/slang-utils/create-parser.ts +++ b/src/slang-utils/create-parser.ts @@ -53,26 +53,27 @@ const milestoneVersions = [ (milestone) => maxSatisfying(supportedVersions, `<${milestone}`)! ); +function parserAndOutput( + text: string, + version: string +): { parser: Parser; parseOutput: ParseOutput } { + const parser = Parser.create(version); + return { + parser, + parseOutput: parser.parseNonterminal(NonterminalKind.SourceUnit, text) + }; +} + export function createParser( text: string, options: ParserOptions ): { parser: Parser; parseOutput: ParseOutput } { const compiler = maxSatisfying(supportedVersions, options.compiler); - if (compiler) { - const parser = Parser.create(compiler); - return { - parser, - parseOutput: parser.parseNonterminal(NonterminalKind.SourceUnit, text) - }; - } + if (compiler) return parserAndOutput(text, compiler); const inferredRanges: string[] = LanguageFacts.inferLanguageVersions(text); - const parser = Parser.create(inferredRanges[inferredRanges.length - 1]); - const result = { - parser, - parseOutput: parser.parseNonterminal(NonterminalKind.SourceUnit, text) - }; + let result = parserAndOutput(text, inferredRanges[inferredRanges.length - 1]); if (result.parseOutput.isValid()) return result; const inferredMilestones = milestoneVersions.filter((milestone) => @@ -80,11 +81,7 @@ export function createParser( ); for (let i = inferredMilestones.length - 1; i >= 0; i -= 1) { - result.parser = Parser.create(inferredMilestones[i]); - result.parseOutput = result.parser.parseNonterminal( - NonterminalKind.SourceUnit, - text - ); + result = parserAndOutput(text, inferredMilestones[i]); if (result.parseOutput.isValid()) break; } From 725530fe14aabb8f0afe7a5cbab43df5aa6be289 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 13 Jun 2025 11:14:12 +0100 Subject: [PATCH 08/12] removing the logic that added issues for maintainance --- src/slang-utils/create-parser.ts | 63 +++---------------- .../__snapshots__/format.test.js.snap | 5 +- .../AllSolidityFeaturesV0.4.26/format.test.js | 2 +- .../__snapshots__/format.test.js.snap | 5 +- tests/format/BasicIterator/format.test.js | 2 +- .../__snapshots__/format.test.js.snap | 5 +- .../FunctionDefinitionsV0.5.0/format.test.js | 2 +- .../__snapshots__/format.test.js.snap | 5 +- .../FunctionDefinitionsv0.5.0/format.test.js | 2 +- .../IndexOf/__snapshots__/format.test.js.snap | 5 +- tests/format/IndexOf/format.test.js | 2 +- tests/unit/slang-utils/create-parser.test.js | 4 +- 12 files changed, 31 insertions(+), 71 deletions(-) diff --git a/src/slang-utils/create-parser.ts b/src/slang-utils/create-parser.ts index 06088dfe6..76aa19afe 100644 --- a/src/slang-utils/create-parser.ts +++ b/src/slang-utils/create-parser.ts @@ -9,50 +9,6 @@ import type { AstNode } from '../slang-nodes/types.js'; const supportedVersions = LanguageFacts.allVersions(); -// This list was retrieved from Slang's documentation. -// https://nomicfoundation.github.io/slang/latest/solidity-grammar/supported-versions/ -// TODO: use the run-time functionality if NomicFoundation decides to expose -// this information directly. -const milestoneVersions = [ - '0.4.14', - '0.4.16', - '0.4.21', - '0.4.22', - '0.4.25', - '0.5.0', - '0.5.3', - '0.5.5', - '0.5.8', - '0.5.10', - '0.5.14', - '0.6.0', - '0.6.2', - '0.6.5', - '0.6.7', - '0.6.8', - '0.6.11', - '0.7.0', - '0.7.1', - '0.7.4', - '0.8.0', - '0.8.4', - '0.8.8', - '0.8.13', - '0.8.18', - '0.8.19', - '0.8.22', - '0.8.27', - '0.8.29' -].map( - // Since we are aiming to find the highest compatible Language version, we - // define a milestone as the highest possible supported version that is - // smaller than the original milestone taken from the documentation. - // This has an extra cost in execution time and the actual list could be - // hardcoded, but the calculation is done only once and it's easier to - // maintain. - (milestone) => maxSatisfying(supportedVersions, `<${milestone}`)! -); - function parserAndOutput( text: string, version: string @@ -73,17 +29,16 @@ export function createParser( const inferredRanges: string[] = LanguageFacts.inferLanguageVersions(text); - let result = parserAndOutput(text, inferredRanges[inferredRanges.length - 1]); - if (result.parseOutput.isValid()) return result; - - const inferredMilestones = milestoneVersions.filter((milestone) => - inferredRanges.includes(milestone) + const result = parserAndOutput( + text, + inferredRanges[inferredRanges.length - 1] ); - - for (let i = inferredMilestones.length - 1; i >= 0; i -= 1) { - result = parserAndOutput(text, inferredMilestones[i]); - if (result.parseOutput.isValid()) break; - } + if (!result.parseOutput.isValid()) + throw new Error( + `Based on the pragma statements, we inferred your code to be using Solidity version ${ + result.parser.languageVersion + }. If you would like to change that, update the pragmas in your source file, or specify a version in \`.prettierrc\` or VSCode's \`settings.json\`.` + ); return result; } diff --git a/tests/format/AllSolidityFeaturesV0.4.26/__snapshots__/format.test.js.snap b/tests/format/AllSolidityFeaturesV0.4.26/__snapshots__/format.test.js.snap index 4dc632449..ee0607181 100644 --- a/tests/format/AllSolidityFeaturesV0.4.26/__snapshots__/format.test.js.snap +++ b/tests/format/AllSolidityFeaturesV0.4.26/__snapshots__/format.test.js.snap @@ -1,7 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`AllSolidityFeatures.sol format 1`] = ` +exports[`AllSolidityFeatures.sol - {"compiler":"0.4.26"} format 1`] = ` ====================================options===================================== +compiler: "0.4.26" parsers: ["slang"] printWidth: 80 | printWidth diff --git a/tests/format/AllSolidityFeaturesV0.4.26/format.test.js b/tests/format/AllSolidityFeaturesV0.4.26/format.test.js index 6021bbb0d..85c6f9a33 100644 --- a/tests/format/AllSolidityFeaturesV0.4.26/format.test.js +++ b/tests/format/AllSolidityFeaturesV0.4.26/format.test.js @@ -1 +1 @@ -runFormatTest(import.meta, ['slang']); +runFormatTest(import.meta, ['slang'], { compiler: '0.4.26' }); diff --git a/tests/format/BasicIterator/__snapshots__/format.test.js.snap b/tests/format/BasicIterator/__snapshots__/format.test.js.snap index db55d79c3..1fdd5e387 100644 --- a/tests/format/BasicIterator/__snapshots__/format.test.js.snap +++ b/tests/format/BasicIterator/__snapshots__/format.test.js.snap @@ -1,7 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`BasicIterator.sol format 1`] = ` +exports[`BasicIterator.sol - {"compiler":"0.4.26"} format 1`] = ` ====================================options===================================== +compiler: "0.4.26" parsers: ["slang"] printWidth: 80 | printWidth diff --git a/tests/format/BasicIterator/format.test.js b/tests/format/BasicIterator/format.test.js index 6021bbb0d..85c6f9a33 100644 --- a/tests/format/BasicIterator/format.test.js +++ b/tests/format/BasicIterator/format.test.js @@ -1 +1 @@ -runFormatTest(import.meta, ['slang']); +runFormatTest(import.meta, ['slang'], { compiler: '0.4.26' }); diff --git a/tests/format/FunctionDefinitionsV0.5.0/__snapshots__/format.test.js.snap b/tests/format/FunctionDefinitionsV0.5.0/__snapshots__/format.test.js.snap index 625f22065..e010c16f7 100644 --- a/tests/format/FunctionDefinitionsV0.5.0/__snapshots__/format.test.js.snap +++ b/tests/format/FunctionDefinitionsV0.5.0/__snapshots__/format.test.js.snap @@ -1,7 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`FunctionDefinitions.sol format 1`] = ` +exports[`FunctionDefinitions.sol - {"compiler":"0.5.17"} format 1`] = ` ====================================options===================================== +compiler: "0.5.17" parsers: ["slang"] printWidth: 80 | printWidth diff --git a/tests/format/FunctionDefinitionsV0.5.0/format.test.js b/tests/format/FunctionDefinitionsV0.5.0/format.test.js index 6021bbb0d..80d418f68 100644 --- a/tests/format/FunctionDefinitionsV0.5.0/format.test.js +++ b/tests/format/FunctionDefinitionsV0.5.0/format.test.js @@ -1 +1 @@ -runFormatTest(import.meta, ['slang']); +runFormatTest(import.meta, ['slang'], { compiler: '0.5.17' }); diff --git a/tests/format/FunctionDefinitionsv0.5.0/__snapshots__/format.test.js.snap b/tests/format/FunctionDefinitionsv0.5.0/__snapshots__/format.test.js.snap index 625f22065..e010c16f7 100644 --- a/tests/format/FunctionDefinitionsv0.5.0/__snapshots__/format.test.js.snap +++ b/tests/format/FunctionDefinitionsv0.5.0/__snapshots__/format.test.js.snap @@ -1,7 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`FunctionDefinitions.sol format 1`] = ` +exports[`FunctionDefinitions.sol - {"compiler":"0.5.17"} format 1`] = ` ====================================options===================================== +compiler: "0.5.17" parsers: ["slang"] printWidth: 80 | printWidth diff --git a/tests/format/FunctionDefinitionsv0.5.0/format.test.js b/tests/format/FunctionDefinitionsv0.5.0/format.test.js index 6021bbb0d..80d418f68 100644 --- a/tests/format/FunctionDefinitionsv0.5.0/format.test.js +++ b/tests/format/FunctionDefinitionsv0.5.0/format.test.js @@ -1 +1 @@ -runFormatTest(import.meta, ['slang']); +runFormatTest(import.meta, ['slang'], { compiler: '0.5.17' }); diff --git a/tests/format/IndexOf/__snapshots__/format.test.js.snap b/tests/format/IndexOf/__snapshots__/format.test.js.snap index 791069d51..01afcc0b0 100644 --- a/tests/format/IndexOf/__snapshots__/format.test.js.snap +++ b/tests/format/IndexOf/__snapshots__/format.test.js.snap @@ -1,7 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`IndexOf.sol format 1`] = ` +exports[`IndexOf.sol - {"compiler":"0.4.26"} format 1`] = ` ====================================options===================================== +compiler: "0.4.26" parsers: ["slang"] printWidth: 80 | printWidth diff --git a/tests/format/IndexOf/format.test.js b/tests/format/IndexOf/format.test.js index 6021bbb0d..85c6f9a33 100644 --- a/tests/format/IndexOf/format.test.js +++ b/tests/format/IndexOf/format.test.js @@ -1 +1 @@ -runFormatTest(import.meta, ['slang']); +runFormatTest(import.meta, ['slang'], { compiler: '0.4.26' }); diff --git a/tests/unit/slang-utils/create-parser.test.js b/tests/unit/slang-utils/create-parser.test.js index d67349d19..1bdc6a88e 100644 --- a/tests/unit/slang-utils/create-parser.test.js +++ b/tests/unit/slang-utils/create-parser.test.js @@ -102,8 +102,8 @@ describe('inferLanguage', function () { ({ parser } = createParser(`contract Foo {}`, options)); expect(parser.languageVersion).toEqual(latestSupportedVersion); - ({ parser } = createParser(`contract Foo {byte bar;}`, options)); - expect(parser.languageVersion).toEqual('0.7.6'); + // ({ parser } = createParser(`contract Foo {byte bar;}`, options)); + // expect(parser.languageVersion).toEqual('0.7.6'); }); test('should use compiler option if given', function () { From 080e9406be0de73d441f98829d86a9d7ed351e6c Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 13 Jun 2025 21:10:53 +0100 Subject: [PATCH 09/12] improving the error messages when the language inference fails because of the syntax. --- src/slang-utils/create-parser.ts | 23 +++++++++++++++++++---- src/slangSolidityParser.ts | 19 ++++++++----------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/slang-utils/create-parser.ts b/src/slang-utils/create-parser.ts index 76aa19afe..efae3e4d5 100644 --- a/src/slang-utils/create-parser.ts +++ b/src/slang-utils/create-parser.ts @@ -25,19 +25,34 @@ export function createParser( options: ParserOptions ): { parser: Parser; parseOutput: ParseOutput } { const compiler = maxSatisfying(supportedVersions, options.compiler); - if (compiler) return parserAndOutput(text, compiler); + if (compiler) { + const result = parserAndOutput(text, compiler); + if (!result.parseOutput.isValid()) + throw new Error( + 'We encoutered the following syntax error:\n\n\t' + + result.parseOutput.errors()[0].message + + '\n\nBased on the compiler option provided, we inferred your code to be using Solidity version ' + + result.parser.languageVersion + + '. If you would like to change that, specify a different version in your `.prettierrc` file.' + ); + + return result; + } const inferredRanges: string[] = LanguageFacts.inferLanguageVersions(text); const result = parserAndOutput( text, inferredRanges[inferredRanges.length - 1] ); + if (!result.parseOutput.isValid()) throw new Error( - `Based on the pragma statements, we inferred your code to be using Solidity version ${ - result.parser.languageVersion - }. If you would like to change that, update the pragmas in your source file, or specify a version in \`.prettierrc\` or VSCode's \`settings.json\`.` + 'We encoutered the following syntax error:\n\n\t' + + result.parseOutput.errors()[0].message + + '\n\nBased on the pragma statements, we inferred your code to be using Solidity version ' + + result.parser.languageVersion + + '. If you would like to change that, update the pragmas in your source file, or specify a version in your `.prettierrc` file.' ); return result; diff --git a/src/slangSolidityParser.ts b/src/slangSolidityParser.ts index 43c66f9af..cb40ce145 100644 --- a/src/slangSolidityParser.ts +++ b/src/slangSolidityParser.ts @@ -13,15 +13,12 @@ export default function parse( ): AstNode { const { parser, parseOutput } = createParser(text, options); - if (parseOutput.isValid()) { - // We update the compiler version by the inferred one. - options.compiler = parser.languageVersion; - const parsed = new SourceUnit( - new SlangSourceUnit(parseOutput.tree.asNonterminalNode()), - options - ); - clearOffsets(); - return parsed; - } - throw new Error(parseOutput.errors()[0].message); + // We update the compiler version by the inferred one. + options.compiler = parser.languageVersion; + const parsed = new SourceUnit( + new SlangSourceUnit(parseOutput.tree.asNonterminalNode()), + options + ); + clearOffsets(); + return parsed; } From 4c5f8ab360016c54130054781aa3a42ae9c5d2f1 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 16 Jun 2025 09:18:09 +0100 Subject: [PATCH 10/12] using the earliest version with the exception when there are no pragma statements --- src/slang-utils/create-parser.ts | 8 +++++--- tests/unit/slang-utils/create-parser.test.js | 13 ++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/slang-utils/create-parser.ts b/src/slang-utils/create-parser.ts index efae3e4d5..a5195367f 100644 --- a/src/slang-utils/create-parser.ts +++ b/src/slang-utils/create-parser.ts @@ -1,13 +1,14 @@ import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { Parser } from '@nomicfoundation/slang/parser'; import { LanguageFacts } from '@nomicfoundation/slang/utils'; -import { maxSatisfying } from 'semver'; +import { minSatisfying } from 'semver'; import type { ParseOutput } from '@nomicfoundation/slang/parser'; import type { ParserOptions } from 'prettier'; import type { AstNode } from '../slang-nodes/types.js'; const supportedVersions = LanguageFacts.allVersions(); +const supportedLength = supportedVersions.length; function parserAndOutput( text: string, @@ -24,7 +25,7 @@ export function createParser( text: string, options: ParserOptions ): { parser: Parser; parseOutput: ParseOutput } { - const compiler = maxSatisfying(supportedVersions, options.compiler); + const compiler = minSatisfying(supportedVersions, options.compiler); if (compiler) { const result = parserAndOutput(text, compiler); @@ -40,10 +41,11 @@ export function createParser( return result; } const inferredRanges: string[] = LanguageFacts.inferLanguageVersions(text); + const inferredLength = inferredRanges.length; const result = parserAndOutput( text, - inferredRanges[inferredRanges.length - 1] + inferredRanges[inferredLength === supportedLength ? inferredLength - 1 : 0] ); if (!result.parseOutput.isValid()) diff --git a/tests/unit/slang-utils/create-parser.test.js b/tests/unit/slang-utils/create-parser.test.js index 1bdc6a88e..d8c04cb08 100644 --- a/tests/unit/slang-utils/create-parser.test.js +++ b/tests/unit/slang-utils/create-parser.test.js @@ -9,7 +9,7 @@ describe('inferLanguage', function () { { description: 'Caret range', source: `pragma solidity ^0.7.0;`, - version: '0.7.6' + version: '0.7.0' }, { description: 'Pinned version', @@ -83,8 +83,10 @@ describe('inferLanguage', function () { { description: 'should use the latest version if the range is outside the supported versions', - source: `pragma solidity ^0.8.27;`, - version: latestSupportedVersion + source: `pragma solidity ^10.0.0;`, + version: latestSupportedVersion, + // TODO: unskip this test when slack fixes the error with ranges outside the supported versions. + skip: true } ]; @@ -99,9 +101,6 @@ describe('inferLanguage', function () { let { parser } = createParser(`contract Foo {}`, options); expect(parser.languageVersion).toEqual(latestSupportedVersion); - ({ parser } = createParser(`contract Foo {}`, options)); - expect(parser.languageVersion).toEqual(latestSupportedVersion); - // ({ parser } = createParser(`contract Foo {byte bar;}`, options)); // expect(parser.languageVersion).toEqual('0.7.6'); }); @@ -118,7 +117,7 @@ describe('inferLanguage', function () { expect(parser.languageVersion).toEqual('0.8.2'); ({ parser } = createParser(`pragma solidity ^0.8.0;`, {})); - expect(parser.languageVersion).toEqual(latestSupportedVersion); + expect(parser.languageVersion).toEqual('0.8.0'); }); test('should throw an error if there are incompatible ranges', function () { From ff18c5d2fe2f9f273e2a07c6b0f71285347e7d95 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 16 Jun 2025 18:57:17 +0100 Subject: [PATCH 11/12] passing tests --- .../__snapshots__/format.test.js.snap | 5 ++-- tests/format/MemberAccess/format.test.js | 2 +- .../__snapshots__/format.test.js.snap | 5 ++-- tests/format/SimpleStorage/format.test.js | 2 +- .../__snapshots__/format.test.js.snap | 26 ++++++++++++------- tests/format/StyleGuide/format.test.js | 2 +- .../strings/__snapshots__/format.test.js.snap | 5 ++-- tests/format/strings/format.test.js | 2 +- 8 files changed, 30 insertions(+), 19 deletions(-) diff --git a/tests/format/MemberAccess/__snapshots__/format.test.js.snap b/tests/format/MemberAccess/__snapshots__/format.test.js.snap index f036c9128..f3d07202b 100644 --- a/tests/format/MemberAccess/__snapshots__/format.test.js.snap +++ b/tests/format/MemberAccess/__snapshots__/format.test.js.snap @@ -1,7 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`MemberAccess.sol format 1`] = ` +exports[`MemberAccess.sol - {"compiler":"0.6.12"} format 1`] = ` ====================================options===================================== +compiler: "0.6.12" parsers: ["slang"] printWidth: 80 | printWidth diff --git a/tests/format/MemberAccess/format.test.js b/tests/format/MemberAccess/format.test.js index 6021bbb0d..569e4c7b0 100644 --- a/tests/format/MemberAccess/format.test.js +++ b/tests/format/MemberAccess/format.test.js @@ -1 +1 @@ -runFormatTest(import.meta, ['slang']); +runFormatTest(import.meta, ['slang'], { compiler: '0.6.12' }); diff --git a/tests/format/SimpleStorage/__snapshots__/format.test.js.snap b/tests/format/SimpleStorage/__snapshots__/format.test.js.snap index db687b891..99944356e 100644 --- a/tests/format/SimpleStorage/__snapshots__/format.test.js.snap +++ b/tests/format/SimpleStorage/__snapshots__/format.test.js.snap @@ -1,7 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`SimpleStorage.sol format 1`] = ` +exports[`SimpleStorage.sol - {"compiler":"0.4.26"} format 1`] = ` ====================================options===================================== +compiler: "0.4.26" parsers: ["slang"] printWidth: 80 | printWidth diff --git a/tests/format/SimpleStorage/format.test.js b/tests/format/SimpleStorage/format.test.js index 6021bbb0d..85c6f9a33 100644 --- a/tests/format/SimpleStorage/format.test.js +++ b/tests/format/SimpleStorage/format.test.js @@ -1 +1 @@ -runFormatTest(import.meta, ['slang']); +runFormatTest(import.meta, ['slang'], { compiler: '0.4.26' }); diff --git a/tests/format/StyleGuide/__snapshots__/format.test.js.snap b/tests/format/StyleGuide/__snapshots__/format.test.js.snap index b8014781f..8ceed378d 100644 --- a/tests/format/StyleGuide/__snapshots__/format.test.js.snap +++ b/tests/format/StyleGuide/__snapshots__/format.test.js.snap @@ -1,7 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`BlankLines.sol format 1`] = ` +exports[`BlankLines.sol - {"compiler":"0.5.17"} format 1`] = ` ====================================options===================================== +compiler: "0.5.17" parsers: ["slang"] printWidth: 80 | printWidth @@ -46,8 +47,9 @@ contract A { ================================================================================ `; -exports[`ControlStructures.sol format 1`] = ` +exports[`ControlStructures.sol - {"compiler":"0.5.17"} format 1`] = ` ====================================options===================================== +compiler: "0.5.17" parsers: ["slang"] printWidth: 80 | printWidth @@ -167,8 +169,9 @@ contract ControlStructures { ================================================================================ `; -exports[`FunctionDeclaration.sol format 1`] = ` +exports[`FunctionDeclaration.sol - {"compiler":"0.5.17"} format 1`] = ` ====================================options===================================== +compiler: "0.5.17" parsers: ["slang"] printWidth: 80 | printWidth @@ -458,8 +461,9 @@ contract X is B, C, D { ================================================================================ `; -exports[`Mappings.sol format 1`] = ` +exports[`Mappings.sol - {"compiler":"0.5.17"} format 1`] = ` ====================================options===================================== +compiler: "0.5.17" parsers: ["slang"] printWidth: 80 | printWidth @@ -487,8 +491,9 @@ contract Mappings { ================================================================================ `; -exports[`MaximumLineLength.sol format 1`] = ` +exports[`MaximumLineLength.sol - {"compiler":"0.5.17"} format 1`] = ` ====================================options===================================== +compiler: "0.5.17" parsers: ["slang"] printWidth: 80 | printWidth @@ -642,8 +647,9 @@ contract EventDefinitionsAndEventEmitters { ================================================================================ `; -exports[`OtherRecommendations.sol format 1`] = ` +exports[`OtherRecommendations.sol - {"compiler":"0.5.17"} format 1`] = ` ====================================options===================================== +compiler: "0.5.17" parsers: ["slang"] printWidth: 80 | printWidth @@ -709,8 +715,9 @@ contract OtherRecommendations { ================================================================================ `; -exports[`VariableDeclarations.sol format 1`] = ` +exports[`VariableDeclarations.sol - {"compiler":"0.5.17"} format 1`] = ` ====================================options===================================== +compiler: "0.5.17" parsers: ["slang"] printWidth: 80 | printWidth @@ -732,8 +739,9 @@ contract VariableDeclarations { ================================================================================ `; -exports[`WhitespaceInExpressions.sol format 1`] = ` +exports[`WhitespaceInExpressions.sol - {"compiler":"0.5.17"} format 1`] = ` ====================================options===================================== +compiler: "0.5.17" parsers: ["slang"] printWidth: 80 | printWidth diff --git a/tests/format/StyleGuide/format.test.js b/tests/format/StyleGuide/format.test.js index 6021bbb0d..80d418f68 100644 --- a/tests/format/StyleGuide/format.test.js +++ b/tests/format/StyleGuide/format.test.js @@ -1 +1 @@ -runFormatTest(import.meta, ['slang']); +runFormatTest(import.meta, ['slang'], { compiler: '0.5.17' }); diff --git a/tests/format/strings/__snapshots__/format.test.js.snap b/tests/format/strings/__snapshots__/format.test.js.snap index 397392bf3..09408f084 100644 --- a/tests/format/strings/__snapshots__/format.test.js.snap +++ b/tests/format/strings/__snapshots__/format.test.js.snap @@ -1,7 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`strings.sol format 1`] = ` +exports[`strings.sol - {"compiler":"0.4.26"} format 1`] = ` ====================================options===================================== +compiler: "0.4.26" parsers: ["slang"] printWidth: 80 | printWidth diff --git a/tests/format/strings/format.test.js b/tests/format/strings/format.test.js index 6021bbb0d..85c6f9a33 100644 --- a/tests/format/strings/format.test.js +++ b/tests/format/strings/format.test.js @@ -1 +1 @@ -runFormatTest(import.meta, ['slang']); +runFormatTest(import.meta, ['slang'], { compiler: '0.4.26' }); From a48c29a3252b1f5e442c1bd2dc5198d060a976ae Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 17 Jun 2025 15:36:14 +0100 Subject: [PATCH 12/12] Fix typos and adding a check for an empty array --- src/slang-utils/create-parser.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/slang-utils/create-parser.ts b/src/slang-utils/create-parser.ts index a5195367f..8f511d739 100644 --- a/src/slang-utils/create-parser.ts +++ b/src/slang-utils/create-parser.ts @@ -31,11 +31,11 @@ export function createParser( if (!result.parseOutput.isValid()) throw new Error( - 'We encoutered the following syntax error:\n\n\t' + - result.parseOutput.errors()[0].message + - '\n\nBased on the compiler option provided, we inferred your code to be using Solidity version ' + - result.parser.languageVersion + - '. If you would like to change that, specify a different version in your `.prettierrc` file.' + `We encountered the following syntax error:\n\n\t${ + result.parseOutput.errors()[0].message + }\n\nBased on the compiler option provided, we inferred your code to be using Solidity version ${ + result.parser.languageVersion + }. If you would like to change that, specify a different version in your \`.prettierrc\` file.` ); return result; @@ -43,6 +43,11 @@ export function createParser( const inferredRanges: string[] = LanguageFacts.inferLanguageVersions(text); const inferredLength = inferredRanges.length; + if (inferredLength === 0) { + throw new Error( + `We couldn't infer a Solidity version base on the pragma statements in your code. If you would like to change that, update the pragmas in your source file, or specify a version in your \`.prettierrc\` file.` + ); + } const result = parserAndOutput( text, inferredRanges[inferredLength === supportedLength ? inferredLength - 1 : 0] @@ -50,11 +55,11 @@ export function createParser( if (!result.parseOutput.isValid()) throw new Error( - 'We encoutered the following syntax error:\n\n\t' + - result.parseOutput.errors()[0].message + - '\n\nBased on the pragma statements, we inferred your code to be using Solidity version ' + - result.parser.languageVersion + - '. If you would like to change that, update the pragmas in your source file, or specify a version in your `.prettierrc` file.' + `We encountered the following syntax error:\n\n\t${ + result.parseOutput.errors()[0].message + }\n\nBased on the pragma statements, we inferred your code to be using Solidity version ${ + result.parser.languageVersion + }. If you would like to change that, update the pragmas in your source file, or specify a version in your \`.prettierrc\` file.` ); return result;