diff --git a/.c8rc b/.c8rc index 8510c2fd2..315fdc955 100644 --- a/.c8rc +++ b/.c8rc @@ -5,7 +5,7 @@ "functions": 90, "statements": 90, "exclude": ["/node_modules/"], - "include": ["src/**/*.ts"], + "include": ["src/**/*.ts", "variant-coverage/**/*.ts"], "reporter": ["lcov", "text"], "temp-dir": "./coverage/" } diff --git a/eslint.config.mjs b/eslint.config.mjs index 94a8a4ecd..769e54f6b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -18,6 +18,7 @@ const compat = new FlatCompat({ export default [ { ignores: [ + 'variant-coverage/**/*.ts', 'coverage/**/*.js', 'dist/**/*.cjs', 'dist/**/*.js', diff --git a/src/slang-nodes/ArgumentsDeclaration.ts b/src/slang-nodes/ArgumentsDeclaration.ts index 5af16a582..106dae3c6 100644 --- a/src/slang-nodes/ArgumentsDeclaration.ts +++ b/src/slang-nodes/ArgumentsDeclaration.ts @@ -1,5 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { PositionalArgumentsDeclaration } from './PositionalArgumentsDeclaration.js'; import { NamedArgumentsDeclaration } from './NamedArgumentsDeclaration.js'; @@ -8,20 +9,13 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.ArgumentsDeclaration['variant'], - collected: CollectedMetadata, - options: ParserOptions -): ArgumentsDeclaration['variant'] { - if (variant instanceof ast.PositionalArgumentsDeclaration) { - return new PositionalArgumentsDeclaration(variant, collected, options); - } - if (variant instanceof ast.NamedArgumentsDeclaration) { - return new NamedArgumentsDeclaration(variant, collected, options); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.ArgumentsDeclaration, + ArgumentsDeclaration +>([ + [ast.PositionalArgumentsDeclaration, PositionalArgumentsDeclaration], + [ast.NamedArgumentsDeclaration, NamedArgumentsDeclaration] +]); export class ArgumentsDeclaration extends SlangNode { readonly kind = NonterminalKind.ArgumentsDeclaration; diff --git a/src/slang-nodes/ContractMember.ts b/src/slang-nodes/ContractMember.ts index c6ec4fa9a..767faa39c 100644 --- a/src/slang-nodes/ContractMember.ts +++ b/src/slang-nodes/ContractMember.ts @@ -1,5 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { UsingDirective } from './UsingDirective.js'; import { FunctionDefinition } from './FunctionDefinition.js'; @@ -19,53 +20,24 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.ContractMember['variant'], - collected: CollectedMetadata, - options: ParserOptions -): ContractMember['variant'] { - if (variant instanceof ast.UsingDirective) { - return new UsingDirective(variant, collected, options); - } - if (variant instanceof ast.FunctionDefinition) { - return new FunctionDefinition(variant, collected, options); - } - if (variant instanceof ast.ConstructorDefinition) { - return new ConstructorDefinition(variant, collected, options); - } - if (variant instanceof ast.ReceiveFunctionDefinition) { - return new ReceiveFunctionDefinition(variant, collected, options); - } - if (variant instanceof ast.FallbackFunctionDefinition) { - return new FallbackFunctionDefinition(variant, collected, options); - } - if (variant instanceof ast.UnnamedFunctionDefinition) { - return new UnnamedFunctionDefinition(variant, collected, options); - } - if (variant instanceof ast.ModifierDefinition) { - return new ModifierDefinition(variant, collected, options); - } - if (variant instanceof ast.StructDefinition) { - return new StructDefinition(variant, collected, options); - } - if (variant instanceof ast.EnumDefinition) { - return new EnumDefinition(variant, collected); - } - if (variant instanceof ast.EventDefinition) { - return new EventDefinition(variant, collected, options); - } - if (variant instanceof ast.StateVariableDefinition) { - return new StateVariableDefinition(variant, collected, options); - } - if (variant instanceof ast.ErrorDefinition) { - return new ErrorDefinition(variant, collected, options); - } - if (variant instanceof ast.UserDefinedValueTypeDefinition) { - return new UserDefinedValueTypeDefinition(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.ContractMember, + ContractMember +>([ + [ast.UsingDirective, UsingDirective], + [ast.FunctionDefinition, FunctionDefinition], + [ast.ConstructorDefinition, ConstructorDefinition], + [ast.ReceiveFunctionDefinition, ReceiveFunctionDefinition], + [ast.FallbackFunctionDefinition, FallbackFunctionDefinition], + [ast.UnnamedFunctionDefinition, UnnamedFunctionDefinition], + [ast.ModifierDefinition, ModifierDefinition], + [ast.StructDefinition, StructDefinition], + [ast.EnumDefinition, EnumDefinition], + [ast.EventDefinition, EventDefinition], + [ast.StateVariableDefinition, StateVariableDefinition], + [ast.ErrorDefinition, ErrorDefinition], + [ast.UserDefinedValueTypeDefinition, UserDefinedValueTypeDefinition] +]); export class ContractMember extends SlangNode { readonly kind = NonterminalKind.ContractMember; diff --git a/src/slang-nodes/ContractSpecifier.ts b/src/slang-nodes/ContractSpecifier.ts index 6f75ef231..fc18129bf 100644 --- a/src/slang-nodes/ContractSpecifier.ts +++ b/src/slang-nodes/ContractSpecifier.ts @@ -1,5 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { InheritanceSpecifier } from './InheritanceSpecifier.js'; import { StorageLayoutSpecifier } from './StorageLayoutSpecifier.js'; @@ -8,20 +9,13 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.ContractSpecifier['variant'], - collected: CollectedMetadata, - options: ParserOptions -): ContractSpecifier['variant'] { - if (variant instanceof ast.InheritanceSpecifier) { - return new InheritanceSpecifier(variant, collected, options); - } - if (variant instanceof ast.StorageLayoutSpecifier) { - return new StorageLayoutSpecifier(variant, collected, options); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.ContractSpecifier, + ContractSpecifier +>([ + [ast.InheritanceSpecifier, InheritanceSpecifier], + [ast.StorageLayoutSpecifier, StorageLayoutSpecifier] +]); export class ContractSpecifier extends SlangNode { readonly kind = NonterminalKind.ContractSpecifier; diff --git a/src/slang-nodes/Expression.ts b/src/slang-nodes/Expression.ts index dea0bf1c0..c8239b971 100644 --- a/src/slang-nodes/Expression.ts +++ b/src/slang-nodes/Expression.ts @@ -3,7 +3,7 @@ import { NonterminalKind, TerminalNode as SlangTerminalNode } from '@nomicfoundation/slang/cst'; -import { extractVariant } from '../slang-utils/extract-variant.js'; +import { createNonterminalVariantCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { AssignmentExpression } from './AssignmentExpression.js'; import { ConditionalExpression } from './ConditionalExpression.js'; @@ -38,95 +38,42 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: Exclude, - collected: CollectedMetadata, - options: ParserOptions -): Expression['variant'] { - if (variant instanceof ast.AssignmentExpression) { - return new AssignmentExpression(variant, collected, options); - } - if (variant instanceof ast.ConditionalExpression) { - return new ConditionalExpression(variant, collected, options); - } - if (variant instanceof ast.OrExpression) { - return new OrExpression(variant, collected, options); - } - if (variant instanceof ast.AndExpression) { - return new AndExpression(variant, collected, options); - } - if (variant instanceof ast.EqualityExpression) { - return new EqualityExpression(variant, collected, options); - } - if (variant instanceof ast.InequalityExpression) { - return new InequalityExpression(variant, collected, options); - } - if (variant instanceof ast.BitwiseOrExpression) { - return new BitwiseOrExpression(variant, collected, options); - } - if (variant instanceof ast.BitwiseXorExpression) { - return new BitwiseXorExpression(variant, collected, options); - } - if (variant instanceof ast.BitwiseAndExpression) { - return new BitwiseAndExpression(variant, collected, options); - } - if (variant instanceof ast.ShiftExpression) { - return new ShiftExpression(variant, collected, options); - } - if (variant instanceof ast.AdditiveExpression) { - return new AdditiveExpression(variant, collected, options); - } - if (variant instanceof ast.MultiplicativeExpression) { - return new MultiplicativeExpression(variant, collected, options); - } - if (variant instanceof ast.ExponentiationExpression) { - return new ExponentiationExpression(variant, collected, options); - } - if (variant instanceof ast.PostfixExpression) { - return new PostfixExpression(variant, collected, options); - } - if (variant instanceof ast.PrefixExpression) { - return new PrefixExpression(variant, collected, options); - } - if (variant instanceof ast.FunctionCallExpression) { - return new FunctionCallExpression(variant, collected, options); - } - if (variant instanceof ast.CallOptionsExpression) { - return new CallOptionsExpression(variant, collected, options); - } - if (variant instanceof ast.MemberAccessExpression) { - return new MemberAccessExpression(variant, collected, options); - } - if (variant instanceof ast.IndexAccessExpression) { - return new IndexAccessExpression(variant, collected, options); - } - if (variant instanceof ast.NewExpression) { - return new NewExpression(variant, collected, options); - } - if (variant instanceof ast.TupleExpression) { - return new TupleExpression(variant, collected, options); - } - if (variant instanceof ast.TypeExpression) { - return new TypeExpression(variant, collected, options); - } - if (variant instanceof ast.ArrayExpression) { - return new ArrayExpression(variant, collected, options); - } - if (variant instanceof ast.HexNumberExpression) { - return new HexNumberExpression(variant, collected); - } - if (variant instanceof ast.DecimalNumberExpression) { - return new DecimalNumberExpression(variant, collected); - } - if (variant instanceof ast.StringExpression) { - return extractVariant(new StringExpression(variant, collected, options)); - } - if (variant instanceof ast.ElementaryType) { - return extractVariant(new ElementaryType(variant, collected)); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantCreator< + ast.Expression, + Expression +>( + [ + [ast.AssignmentExpression, AssignmentExpression], + [ast.ConditionalExpression, ConditionalExpression], + [ast.OrExpression, OrExpression], + [ast.AndExpression, AndExpression], + [ast.EqualityExpression, EqualityExpression], + [ast.InequalityExpression, InequalityExpression], + [ast.BitwiseOrExpression, BitwiseOrExpression], + [ast.BitwiseXorExpression, BitwiseXorExpression], + [ast.BitwiseAndExpression, BitwiseAndExpression], + [ast.ShiftExpression, ShiftExpression], + [ast.AdditiveExpression, AdditiveExpression], + [ast.MultiplicativeExpression, MultiplicativeExpression], + [ast.ExponentiationExpression, ExponentiationExpression], + [ast.PostfixExpression, PostfixExpression], + [ast.PrefixExpression, PrefixExpression], + [ast.FunctionCallExpression, FunctionCallExpression], + [ast.CallOptionsExpression, CallOptionsExpression], + [ast.MemberAccessExpression, MemberAccessExpression], + [ast.IndexAccessExpression, IndexAccessExpression], + [ast.NewExpression, NewExpression], + [ast.TupleExpression, TupleExpression], + [ast.TypeExpression, TypeExpression], + [ast.ArrayExpression, ArrayExpression], + [ast.HexNumberExpression, HexNumberExpression], + [ast.DecimalNumberExpression, DecimalNumberExpression] + ], + [ + [ast.StringExpression, StringExpression], + [ast.ElementaryType, ElementaryType] + ] +); export class Expression extends SlangNode { readonly kind = NonterminalKind.Expression; diff --git a/src/slang-nodes/FallbackFunctionAttribute.ts b/src/slang-nodes/FallbackFunctionAttribute.ts index 9af79da6e..f757e2427 100644 --- a/src/slang-nodes/FallbackFunctionAttribute.ts +++ b/src/slang-nodes/FallbackFunctionAttribute.ts @@ -3,6 +3,7 @@ import { NonterminalKind, TerminalNode as SlangTerminalNode } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { ModifierInvocation } from './ModifierInvocation.js'; import { OverrideSpecifier } from './OverrideSpecifier.js'; @@ -12,20 +13,13 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: Exclude, - collected: CollectedMetadata, - options: ParserOptions -): Exclude { - if (variant instanceof ast.ModifierInvocation) { - return new ModifierInvocation(variant, collected, options); - } - if (variant instanceof ast.OverrideSpecifier) { - return new OverrideSpecifier(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.FallbackFunctionAttribute, + FallbackFunctionAttribute +>([ + [ast.ModifierInvocation, ModifierInvocation], + [ast.OverrideSpecifier, OverrideSpecifier] +]); export class FallbackFunctionAttribute extends SlangNode { readonly kind = NonterminalKind.FallbackFunctionAttribute; diff --git a/src/slang-nodes/ForStatementInitialization.ts b/src/slang-nodes/ForStatementInitialization.ts index bb7adadf5..84ca74ae3 100644 --- a/src/slang-nodes/ForStatementInitialization.ts +++ b/src/slang-nodes/ForStatementInitialization.ts @@ -3,6 +3,7 @@ import { NonterminalKind, TerminalNode as SlangTerminalNode } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { ExpressionStatement } from './ExpressionStatement.js'; import { VariableDeclarationStatement } from './VariableDeclarationStatement.js'; @@ -13,26 +14,14 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: Exclude< - ast.ForStatementInitialization['variant'], - SlangTerminalNode - >, - collected: CollectedMetadata, - options: ParserOptions -): Exclude { - if (variant instanceof ast.ExpressionStatement) { - return new ExpressionStatement(variant, collected, options); - } - if (variant instanceof ast.VariableDeclarationStatement) { - return new VariableDeclarationStatement(variant, collected, options); - } - if (variant instanceof ast.TupleDeconstructionStatement) { - return new TupleDeconstructionStatement(variant, collected, options); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.ForStatementInitialization, + ForStatementInitialization +>([ + [ast.ExpressionStatement, ExpressionStatement], + [ast.VariableDeclarationStatement, VariableDeclarationStatement], + [ast.TupleDeconstructionStatement, TupleDeconstructionStatement] +]); export class ForStatementInitialization extends SlangNode { readonly kind = NonterminalKind.ForStatementInitialization; diff --git a/src/slang-nodes/FunctionAttribute.ts b/src/slang-nodes/FunctionAttribute.ts index fe4b041d9..c946ca94e 100644 --- a/src/slang-nodes/FunctionAttribute.ts +++ b/src/slang-nodes/FunctionAttribute.ts @@ -3,6 +3,7 @@ import { NonterminalKind, TerminalNode as SlangTerminalNode } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { ModifierInvocation } from './ModifierInvocation.js'; import { OverrideSpecifier } from './OverrideSpecifier.js'; @@ -12,20 +13,13 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: Exclude, - collected: CollectedMetadata, - options: ParserOptions -): Exclude { - if (variant instanceof ast.ModifierInvocation) { - return new ModifierInvocation(variant, collected, options); - } - if (variant instanceof ast.OverrideSpecifier) { - return new OverrideSpecifier(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.FunctionAttribute, + FunctionAttribute +>([ + [ast.ModifierInvocation, ModifierInvocation], + [ast.OverrideSpecifier, OverrideSpecifier] +]); export class FunctionAttribute extends SlangNode { readonly kind = NonterminalKind.FunctionAttribute; diff --git a/src/slang-nodes/ImportClause.ts b/src/slang-nodes/ImportClause.ts index 73015ba8d..1421e735d 100644 --- a/src/slang-nodes/ImportClause.ts +++ b/src/slang-nodes/ImportClause.ts @@ -1,5 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { PathImport } from './PathImport.js'; import { NamedImport } from './NamedImport.js'; @@ -9,23 +10,14 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.ImportClause['variant'], - collected: CollectedMetadata, - options: ParserOptions -): ImportClause['variant'] { - if (variant instanceof ast.PathImport) { - return new PathImport(variant, collected, options); - } - if (variant instanceof ast.NamedImport) { - return new NamedImport(variant, collected, options); - } - if (variant instanceof ast.ImportDeconstruction) { - return new ImportDeconstruction(variant, collected, options); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.ImportClause, + ImportClause +>([ + [ast.PathImport, PathImport], + [ast.NamedImport, NamedImport], + [ast.ImportDeconstruction, ImportDeconstruction] +]); export class ImportClause extends SlangNode { readonly kind = NonterminalKind.ImportClause; diff --git a/src/slang-nodes/MappingKeyType.ts b/src/slang-nodes/MappingKeyType.ts index e14a4d56a..523244395 100644 --- a/src/slang-nodes/MappingKeyType.ts +++ b/src/slang-nodes/MappingKeyType.ts @@ -1,25 +1,19 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; -import { extractVariant } from '../slang-utils/extract-variant.js'; +import { createNonterminalVariantCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { ElementaryType } from './ElementaryType.js'; import { IdentifierPath } from './IdentifierPath.js'; import type { CollectedMetadata } from '../types.d.ts'; -function createNonterminalVariant( - variant: ast.MappingKeyType['variant'], - collected: CollectedMetadata -): MappingKeyType['variant'] { - if (variant instanceof ast.ElementaryType) { - return extractVariant(new ElementaryType(variant, collected)); - } - if (variant instanceof ast.IdentifierPath) { - return new IdentifierPath(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantCreator< + ast.MappingKeyType, + MappingKeyType +>( + [[ast.IdentifierPath, IdentifierPath]], + [[ast.ElementaryType, ElementaryType]] +); export class MappingKeyType extends SlangNode { readonly kind = NonterminalKind.MappingKeyType; diff --git a/src/slang-nodes/Pragma.ts b/src/slang-nodes/Pragma.ts index fb8f2610e..86a4ece4d 100644 --- a/src/slang-nodes/Pragma.ts +++ b/src/slang-nodes/Pragma.ts @@ -1,5 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { AbicoderPragma } from './AbicoderPragma.js'; import { ExperimentalPragma } from './ExperimentalPragma.js'; @@ -9,23 +10,14 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.Pragma['variant'], - collected: CollectedMetadata, - options: ParserOptions -): Pragma['variant'] { - if (variant instanceof ast.AbicoderPragma) { - return new AbicoderPragma(variant, collected); - } - if (variant instanceof ast.ExperimentalPragma) { - return new ExperimentalPragma(variant, collected, options); - } - if (variant instanceof ast.VersionPragma) { - return new VersionPragma(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.Pragma, + Pragma +>([ + [ast.AbicoderPragma, AbicoderPragma], + [ast.ExperimentalPragma, ExperimentalPragma], + [ast.VersionPragma, VersionPragma] +]); export class Pragma extends SlangNode { readonly kind = NonterminalKind.Pragma; diff --git a/src/slang-nodes/ReceiveFunctionAttribute.ts b/src/slang-nodes/ReceiveFunctionAttribute.ts index a99bd7750..5e91580ab 100644 --- a/src/slang-nodes/ReceiveFunctionAttribute.ts +++ b/src/slang-nodes/ReceiveFunctionAttribute.ts @@ -3,6 +3,7 @@ import { NonterminalKind, TerminalNode as SlangTerminalNode } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { ModifierInvocation } from './ModifierInvocation.js'; import { OverrideSpecifier } from './OverrideSpecifier.js'; @@ -12,20 +13,13 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: Exclude, - collected: CollectedMetadata, - options: ParserOptions -): Exclude { - if (variant instanceof ast.ModifierInvocation) { - return new ModifierInvocation(variant, collected, options); - } - if (variant instanceof ast.OverrideSpecifier) { - return new OverrideSpecifier(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.ReceiveFunctionAttribute, + ReceiveFunctionAttribute +>([ + [ast.ModifierInvocation, ModifierInvocation], + [ast.OverrideSpecifier, OverrideSpecifier] +]); export class ReceiveFunctionAttribute extends SlangNode { readonly kind = NonterminalKind.ReceiveFunctionAttribute; diff --git a/src/slang-nodes/SourceUnitMember.ts b/src/slang-nodes/SourceUnitMember.ts index 95ab0c96e..d545dc29d 100644 --- a/src/slang-nodes/SourceUnitMember.ts +++ b/src/slang-nodes/SourceUnitMember.ts @@ -1,5 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { PragmaDirective } from './PragmaDirective.js'; import { ImportDirective } from './ImportDirective.js'; @@ -19,53 +20,24 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.SourceUnitMember['variant'], - collected: CollectedMetadata, - options: ParserOptions -): SourceUnitMember['variant'] { - if (variant instanceof ast.PragmaDirective) { - return new PragmaDirective(variant, collected, options); - } - if (variant instanceof ast.ImportDirective) { - return new ImportDirective(variant, collected, options); - } - if (variant instanceof ast.ContractDefinition) { - return new ContractDefinition(variant, collected, options); - } - if (variant instanceof ast.InterfaceDefinition) { - return new InterfaceDefinition(variant, collected, options); - } - if (variant instanceof ast.LibraryDefinition) { - return new LibraryDefinition(variant, collected, options); - } - if (variant instanceof ast.StructDefinition) { - return new StructDefinition(variant, collected, options); - } - if (variant instanceof ast.EnumDefinition) { - return new EnumDefinition(variant, collected); - } - if (variant instanceof ast.FunctionDefinition) { - return new FunctionDefinition(variant, collected, options); - } - if (variant instanceof ast.ConstantDefinition) { - return new ConstantDefinition(variant, collected, options); - } - if (variant instanceof ast.ErrorDefinition) { - return new ErrorDefinition(variant, collected, options); - } - if (variant instanceof ast.UserDefinedValueTypeDefinition) { - return new UserDefinedValueTypeDefinition(variant, collected); - } - if (variant instanceof ast.UsingDirective) { - return new UsingDirective(variant, collected, options); - } - if (variant instanceof ast.EventDefinition) { - return new EventDefinition(variant, collected, options); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.SourceUnitMember, + SourceUnitMember +>([ + [ast.PragmaDirective, PragmaDirective], + [ast.ImportDirective, ImportDirective], + [ast.ContractDefinition, ContractDefinition], + [ast.InterfaceDefinition, InterfaceDefinition], + [ast.LibraryDefinition, LibraryDefinition], + [ast.StructDefinition, StructDefinition], + [ast.EnumDefinition, EnumDefinition], + [ast.FunctionDefinition, FunctionDefinition], + [ast.ConstantDefinition, ConstantDefinition], + [ast.ErrorDefinition, ErrorDefinition], + [ast.UserDefinedValueTypeDefinition, UserDefinedValueTypeDefinition], + [ast.UsingDirective, UsingDirective], + [ast.EventDefinition, EventDefinition] +]); export class SourceUnitMember extends SlangNode { readonly kind = NonterminalKind.SourceUnitMember; diff --git a/src/slang-nodes/Statement.ts b/src/slang-nodes/Statement.ts index 174ec0a2a..546e04dc4 100644 --- a/src/slang-nodes/Statement.ts +++ b/src/slang-nodes/Statement.ts @@ -1,5 +1,6 @@ -import * as ast from '@nomicfoundation/slang/ast'; +import * as slangAst from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { ExpressionStatement } from './ExpressionStatement.js'; import { VariableDeclarationStatement } from './VariableDeclarationStatement.js'; @@ -23,65 +24,27 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.Statement['variant'], - collected: CollectedMetadata, - options: ParserOptions -): Statement['variant'] { - if (variant instanceof ast.ExpressionStatement) { - return new ExpressionStatement(variant, collected, options); - } - if (variant instanceof ast.VariableDeclarationStatement) { - return new VariableDeclarationStatement(variant, collected, options); - } - if (variant instanceof ast.TupleDeconstructionStatement) { - return new TupleDeconstructionStatement(variant, collected, options); - } - if (variant instanceof ast.IfStatement) { - return new IfStatement(variant, collected, options); - } - if (variant instanceof ast.ForStatement) { - return new ForStatement(variant, collected, options); - } - if (variant instanceof ast.WhileStatement) { - return new WhileStatement(variant, collected, options); - } - if (variant instanceof ast.DoWhileStatement) { - return new DoWhileStatement(variant, collected, options); - } - if (variant instanceof ast.ContinueStatement) { - return new ContinueStatement(variant, collected); - } - if (variant instanceof ast.BreakStatement) { - return new BreakStatement(variant, collected); - } - if (variant instanceof ast.ReturnStatement) { - return new ReturnStatement(variant, collected, options); - } - if (variant instanceof ast.ThrowStatement) { - return new ThrowStatement(variant, collected); - } - if (variant instanceof ast.EmitStatement) { - return new EmitStatement(variant, collected, options); - } - if (variant instanceof ast.TryStatement) { - return new TryStatement(variant, collected, options); - } - if (variant instanceof ast.RevertStatement) { - return new RevertStatement(variant, collected, options); - } - if (variant instanceof ast.AssemblyStatement) { - return new AssemblyStatement(variant, collected, options); - } - if (variant instanceof ast.Block) { - return new Block(variant, collected, options); - } - if (variant instanceof ast.UncheckedBlock) { - return new UncheckedBlock(variant, collected, options); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + slangAst.Statement, + Statement +>([ + [slangAst.ExpressionStatement, ExpressionStatement], + [slangAst.VariableDeclarationStatement, VariableDeclarationStatement], + [slangAst.TupleDeconstructionStatement, TupleDeconstructionStatement], + [slangAst.IfStatement, IfStatement], + [slangAst.ForStatement, ForStatement], + [slangAst.WhileStatement, WhileStatement], + [slangAst.DoWhileStatement, DoWhileStatement], + [slangAst.ContinueStatement, ContinueStatement], + [slangAst.BreakStatement, BreakStatement], + [slangAst.ReturnStatement, ReturnStatement], + [slangAst.ThrowStatement, ThrowStatement], + [slangAst.EmitStatement, EmitStatement], + [slangAst.TryStatement, TryStatement], + [slangAst.RevertStatement, RevertStatement], + [slangAst.AssemblyStatement, AssemblyStatement], + [slangAst.UncheckedBlock, UncheckedBlock] +]); export class Statement extends SlangNode { readonly kind = NonterminalKind.Statement; @@ -106,13 +69,17 @@ export class Statement extends SlangNode { | UncheckedBlock; constructor( - ast: ast.Statement, + ast: slangAst.Statement, collected: CollectedMetadata, options: ParserOptions ) { super(ast, collected); - this.variant = createNonterminalVariant(ast.variant, collected, options); + const variant = ast.variant; + this.variant = + variant instanceof slangAst.Block + ? new Block(variant, collected, options) + : createNonterminalVariant(variant, collected, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/StringExpression.ts b/src/slang-nodes/StringExpression.ts index f13b54423..93913a0fa 100644 --- a/src/slang-nodes/StringExpression.ts +++ b/src/slang-nodes/StringExpression.ts @@ -1,5 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { StringLiteral } from './StringLiteral.js'; import { StringLiterals } from './StringLiterals.js'; @@ -11,29 +12,16 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.StringExpression['variant'], - collected: CollectedMetadata, - options: ParserOptions -): StringExpression['variant'] { - if (variant instanceof ast.StringLiteral) { - return new StringLiteral(variant, collected, options); - } - if (variant instanceof ast.StringLiterals) { - return new StringLiterals(variant, collected, options); - } - if (variant instanceof ast.HexStringLiteral) { - return new HexStringLiteral(variant, collected, options); - } - if (variant instanceof ast.HexStringLiterals) { - return new HexStringLiterals(variant, collected, options); - } - if (variant instanceof ast.UnicodeStringLiterals) { - return new UnicodeStringLiterals(variant, collected, options); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.StringExpression, + StringExpression +>([ + [ast.StringLiteral, StringLiteral], + [ast.StringLiterals, StringLiterals], + [ast.HexStringLiteral, HexStringLiteral], + [ast.HexStringLiterals, HexStringLiterals], + [ast.UnicodeStringLiterals, UnicodeStringLiterals] +]); export class StringExpression extends SlangNode { readonly kind = NonterminalKind.StringExpression; diff --git a/src/slang-nodes/TupleMember.ts b/src/slang-nodes/TupleMember.ts index 23eb06c1c..3b40666ea 100644 --- a/src/slang-nodes/TupleMember.ts +++ b/src/slang-nodes/TupleMember.ts @@ -1,5 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { TypedTupleMember } from './TypedTupleMember.js'; import { UntypedTupleMember } from './UntypedTupleMember.js'; @@ -8,20 +9,13 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.TupleMember['variant'], - collected: CollectedMetadata, - options: ParserOptions -): TupleMember['variant'] { - if (variant instanceof ast.TypedTupleMember) { - return new TypedTupleMember(variant, collected, options); - } - if (variant instanceof ast.UntypedTupleMember) { - return new UntypedTupleMember(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.TupleMember, + TupleMember +>([ + [ast.TypedTupleMember, TypedTupleMember], + [ast.UntypedTupleMember, UntypedTupleMember] +]); export class TupleMember extends SlangNode { readonly kind = NonterminalKind.TupleMember; diff --git a/src/slang-nodes/TypeName.ts b/src/slang-nodes/TypeName.ts index 5f1a31850..faccff25e 100644 --- a/src/slang-nodes/TypeName.ts +++ b/src/slang-nodes/TypeName.ts @@ -1,6 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; -import { extractVariant } from '../slang-utils/extract-variant.js'; +import { createNonterminalVariantCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { ArrayTypeName } from './ArrayTypeName.js'; import { FunctionType } from './FunctionType.js'; @@ -12,29 +12,18 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.TypeName['variant'], - collected: CollectedMetadata, - options: ParserOptions -): TypeName['variant'] { - if (variant instanceof ast.ArrayTypeName) { - return new ArrayTypeName(variant, collected, options); - } - if (variant instanceof ast.FunctionType) { - return new FunctionType(variant, collected, options); - } - if (variant instanceof ast.MappingType) { - return new MappingType(variant, collected, options); - } - if (variant instanceof ast.ElementaryType) { - return extractVariant(new ElementaryType(variant, collected)); - } - if (variant instanceof ast.IdentifierPath) { - return new IdentifierPath(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantCreator< + ast.TypeName, + TypeName +>( + [ + [ast.ArrayTypeName, ArrayTypeName], + [ast.FunctionType, FunctionType], + [ast.MappingType, MappingType], + [ast.IdentifierPath, IdentifierPath] + ], + [[ast.ElementaryType, ElementaryType]] +); export class TypeName extends SlangNode { readonly kind = NonterminalKind.TypeName; diff --git a/src/slang-nodes/UsingClause.ts b/src/slang-nodes/UsingClause.ts index 53f05044a..dcaedb6d9 100644 --- a/src/slang-nodes/UsingClause.ts +++ b/src/slang-nodes/UsingClause.ts @@ -1,24 +1,19 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { IdentifierPath } from './IdentifierPath.js'; import { UsingDeconstruction } from './UsingDeconstruction.js'; import type { CollectedMetadata } from '../types.d.ts'; -function createNonterminalVariant( - variant: ast.UsingClause['variant'], - collected: CollectedMetadata -): UsingClause['variant'] { - if (variant instanceof ast.IdentifierPath) { - return new IdentifierPath(variant, collected); - } - if (variant instanceof ast.UsingDeconstruction) { - return new UsingDeconstruction(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.UsingClause, + UsingClause +>([ + [ast.IdentifierPath, IdentifierPath], + [ast.UsingDeconstruction, UsingDeconstruction] +]); export class UsingClause extends SlangNode { readonly kind = NonterminalKind.UsingClause; diff --git a/src/slang-nodes/VersionExpression.ts b/src/slang-nodes/VersionExpression.ts index ac1339e0d..4b6757835 100644 --- a/src/slang-nodes/VersionExpression.ts +++ b/src/slang-nodes/VersionExpression.ts @@ -1,24 +1,19 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { VersionRange } from './VersionRange.js'; import { VersionTerm } from './VersionTerm.js'; import type { CollectedMetadata } from '../types.d.ts'; -function createNonterminalVariant( - variant: ast.VersionExpression['variant'], - collected: CollectedMetadata -): VersionExpression['variant'] { - if (variant instanceof ast.VersionRange) { - return new VersionRange(variant, collected); - } - if (variant instanceof ast.VersionTerm) { - return new VersionTerm(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.VersionExpression, + VersionExpression +>([ + [ast.VersionRange, VersionRange], + [ast.VersionTerm, VersionTerm] +]); export class VersionExpression extends SlangNode { readonly kind = NonterminalKind.VersionExpression; diff --git a/src/slang-nodes/YulExpression.ts b/src/slang-nodes/YulExpression.ts index 8b1312906..1019d20a6 100644 --- a/src/slang-nodes/YulExpression.ts +++ b/src/slang-nodes/YulExpression.ts @@ -1,6 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; -import { extractVariant } from '../slang-utils/extract-variant.js'; +import { createNonterminalVariantCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { YulFunctionCallExpression } from './YulFunctionCallExpression.js'; import { YulLiteral } from './YulLiteral.js'; @@ -10,23 +10,16 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.YulExpression['variant'], - collected: CollectedMetadata, - options: ParserOptions -): YulExpression['variant'] { - if (variant instanceof ast.YulFunctionCallExpression) { - return new YulFunctionCallExpression(variant, collected, options); - } - if (variant instanceof ast.YulLiteral) { - return extractVariant(new YulLiteral(variant, collected, options)); - } - if (variant instanceof ast.YulPath) { - return new YulPath(variant, collected); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantCreator< + ast.YulExpression, + YulExpression +>( + [ + [ast.YulFunctionCallExpression, YulFunctionCallExpression], + [ast.YulPath, YulPath] + ], + [[ast.YulLiteral, YulLiteral]] +); export class YulExpression extends SlangNode { readonly kind = NonterminalKind.YulExpression; diff --git a/src/slang-nodes/YulLiteral.ts b/src/slang-nodes/YulLiteral.ts index cbfb0194d..1bffdd584 100644 --- a/src/slang-nodes/YulLiteral.ts +++ b/src/slang-nodes/YulLiteral.ts @@ -3,6 +3,7 @@ import { NonterminalKind, TerminalNode as SlangTerminalNode } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { HexStringLiteral } from './HexStringLiteral.js'; import { StringLiteral } from './StringLiteral.js'; @@ -12,20 +13,13 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: Exclude, - collected: CollectedMetadata, - options: ParserOptions -): Exclude { - if (variant instanceof ast.HexStringLiteral) { - return new HexStringLiteral(variant, collected, options); - } - if (variant instanceof ast.StringLiteral) { - return new StringLiteral(variant, collected, options); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.YulLiteral, + YulLiteral +>([ + [ast.HexStringLiteral, HexStringLiteral], + [ast.StringLiteral, StringLiteral] +]); export class YulLiteral extends SlangNode { readonly kind = NonterminalKind.YulLiteral; diff --git a/src/slang-nodes/YulStatement.ts b/src/slang-nodes/YulStatement.ts index 146378877..f0342031f 100644 --- a/src/slang-nodes/YulStatement.ts +++ b/src/slang-nodes/YulStatement.ts @@ -1,6 +1,6 @@ -import * as ast from '@nomicfoundation/slang/ast'; +import * as slangAst from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; -import { extractVariant } from '../slang-utils/extract-variant.js'; +import { createNonterminalVariantCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { YulBlock } from './YulBlock.js'; import { YulFunctionDefinition } from './YulFunctionDefinition.js'; @@ -20,53 +20,25 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.YulStatement['variant'], - collected: CollectedMetadata, - options: ParserOptions -): YulStatement['variant'] { - if (variant instanceof ast.YulBlock) { - return new YulBlock(variant, collected, options); - } - if (variant instanceof ast.YulFunctionDefinition) { - return new YulFunctionDefinition(variant, collected, options); - } - if (variant instanceof ast.YulVariableDeclarationStatement) { - return new YulVariableDeclarationStatement(variant, collected, options); - } - if (variant instanceof ast.YulVariableAssignmentStatement) { - return new YulVariableAssignmentStatement(variant, collected, options); - } - if (variant instanceof ast.YulStackAssignmentStatement) { - return new YulStackAssignmentStatement(variant, collected); - } - if (variant instanceof ast.YulIfStatement) { - return new YulIfStatement(variant, collected, options); - } - if (variant instanceof ast.YulForStatement) { - return new YulForStatement(variant, collected, options); - } - if (variant instanceof ast.YulSwitchStatement) { - return new YulSwitchStatement(variant, collected, options); - } - if (variant instanceof ast.YulLeaveStatement) { - return new YulLeaveStatement(variant, collected); - } - if (variant instanceof ast.YulBreakStatement) { - return new YulBreakStatement(variant, collected); - } - if (variant instanceof ast.YulContinueStatement) { - return new YulContinueStatement(variant, collected); - } - if (variant instanceof ast.YulLabel) { - return new YulLabel(variant, collected); - } - if (variant instanceof ast.YulExpression) { - return extractVariant(new YulExpression(variant, collected, options)); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantCreator< + slangAst.YulStatement, + YulStatement +>( + [ + [slangAst.YulFunctionDefinition, YulFunctionDefinition], + [slangAst.YulVariableDeclarationStatement, YulVariableDeclarationStatement], + [slangAst.YulVariableAssignmentStatement, YulVariableAssignmentStatement], + [slangAst.YulStackAssignmentStatement, YulStackAssignmentStatement], + [slangAst.YulIfStatement, YulIfStatement], + [slangAst.YulForStatement, YulForStatement], + [slangAst.YulSwitchStatement, YulSwitchStatement], + [slangAst.YulLeaveStatement, YulLeaveStatement], + [slangAst.YulBreakStatement, YulBreakStatement], + [slangAst.YulContinueStatement, YulContinueStatement], + [slangAst.YulLabel, YulLabel] + ], + [[slangAst.YulExpression, YulExpression]] +); export class YulStatement extends SlangNode { readonly kind = NonterminalKind.YulStatement; @@ -87,13 +59,17 @@ export class YulStatement extends SlangNode { | YulExpression['variant']; constructor( - ast: ast.YulStatement, + ast: slangAst.YulStatement, collected: CollectedMetadata, options: ParserOptions ) { super(ast, collected); - this.variant = createNonterminalVariant(ast.variant, collected, options); + const variant = ast.variant; + this.variant = + variant instanceof slangAst.YulBlock + ? new YulBlock(variant, collected, options) + : createNonterminalVariant(variant, collected, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/YulSwitchCase.ts b/src/slang-nodes/YulSwitchCase.ts index acde4dbcb..ecee949cd 100644 --- a/src/slang-nodes/YulSwitchCase.ts +++ b/src/slang-nodes/YulSwitchCase.ts @@ -1,5 +1,6 @@ import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; +import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js'; import { SlangNode } from './SlangNode.js'; import { YulDefaultCase } from './YulDefaultCase.js'; import { YulValueCase } from './YulValueCase.js'; @@ -8,20 +9,13 @@ import type { ParserOptions } from 'prettier'; import type { CollectedMetadata } from '../types.d.ts'; import type { AstNode } from './types.d.ts'; -function createNonterminalVariant( - variant: ast.YulSwitchCase['variant'], - collected: CollectedMetadata, - options: ParserOptions -): YulSwitchCase['variant'] { - if (variant instanceof ast.YulDefaultCase) { - return new YulDefaultCase(variant, collected, options); - } - if (variant instanceof ast.YulValueCase) { - return new YulValueCase(variant, collected, options); - } - const exhaustiveCheck: never = variant; - throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`); -} +const createNonterminalVariant = createNonterminalVariantSimpleCreator< + ast.YulSwitchCase, + YulSwitchCase +>([ + [ast.YulDefaultCase, YulDefaultCase], + [ast.YulValueCase, YulValueCase] +]); export class YulSwitchCase extends SlangNode { readonly kind = NonterminalKind.YulSwitchCase; diff --git a/src/slang-utils/create-nonterminal-variant-creator.ts b/src/slang-utils/create-nonterminal-variant-creator.ts new file mode 100644 index 000000000..df515d12d --- /dev/null +++ b/src/slang-utils/create-nonterminal-variant-creator.ts @@ -0,0 +1,63 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { extractVariant } from './extract-variant.js'; + +import type { ParserOptions } from 'prettier'; +import type { AstNode, StrictPolymorphicNode } from '../slang-nodes/types.d.ts'; +import type { + CollectedMetadata, + SlangAstNode, + SlangAstNodeClass +} from '../types.d.ts'; + +type Constructor = new (...args: any) => T; +type ConstructorsFromInstances = U extends any ? Constructor : never; +type SlangPolymorphicNode = Extract; + +type NonterminalVariantFactory< + U extends SlangPolymorphicNode, + T extends StrictPolymorphicNode +> = ( + variant: U['variant'], + collected: CollectedMetadata, + options?: ParserOptions +) => T['variant']; + +export function createNonterminalVariantSimpleCreator< + U extends SlangPolymorphicNode, + T extends StrictPolymorphicNode +>( + constructors: [SlangAstNodeClass, ConstructorsFromInstances][] +): NonterminalVariantFactory { + return (variant, collected, options?) => { + for (const [slangAstClass, constructor] of constructors) { + if (variant instanceof slangAstClass) + return new constructor(variant, collected, options); + } + + throw new Error(`Unexpected variant: ${JSON.stringify(variant)}`); + }; +} + +export function createNonterminalVariantCreator< + U extends SlangPolymorphicNode, + T extends StrictPolymorphicNode +>( + constructors: [SlangAstNodeClass, ConstructorsFromInstances][], + extractVariantConstructors: [ + SlangAstNodeClass, + ConstructorsFromInstances + ][] +): NonterminalVariantFactory { + const simpleCreator = createNonterminalVariantSimpleCreator( + constructors + ); + + return (variant, collected, options?) => { + for (const [slangAstClass, constructor] of extractVariantConstructors) { + if (variant instanceof slangAstClass) + return extractVariant(new constructor(variant, collected, options)); + } + + return simpleCreator(variant, collected, options); + }; +} diff --git a/src/types.d.ts b/src/types.d.ts index 1aff0dff0..6af3f6cca 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -32,3 +32,4 @@ type KeyOfAst = keyof TypeOfAst; type ValuesOf = E[keyof E]; type SlangAstNode = { [k in KeyOfAst]: ValuesOf }[KeyOfAst]; +type SlangAstNodeClass = { [k in KeyOfAst]: TypeOfAst[k] }[KeyOfAst]; diff --git a/test.config.js b/test.config.js index 0e367e7c2..99f93a10e 100644 --- a/test.config.js +++ b/test.config.js @@ -6,7 +6,8 @@ const { __dirname } = createEsmUtils(import.meta); export default { entry: { test: './tests/integration/test-app.js', - 'create-parser': './src/slang-utils/create-parser.js' + 'create-parser': './src/slang-utils/create-parser.js', + 'variant-coverage': './variant-coverage/index.js' }, mode: 'production', bail: true, diff --git a/tests/config/get-variant-coverage.js b/tests/config/get-variant-coverage.js new file mode 100644 index 000000000..0d2bc62b1 --- /dev/null +++ b/tests/config/get-variant-coverage.js @@ -0,0 +1,16 @@ +function getVariantCoverageInternal() { + const entry = process.env.TEST_STANDALONE + ? "../../dist/variant-coverage.js" + : "../../variant-coverage/index.js"; + + return import(entry).then((module) => module.variantCoverage); +} + +let promise; +function getVariantCoverage() { + promise = promise ?? getVariantCoverageInternal(); + + return promise; +} + +export default getVariantCoverage; diff --git a/tests/config/run-format-test.js b/tests/config/run-format-test.js index 284528efa..5d425c556 100644 --- a/tests/config/run-format-test.js +++ b/tests/config/run-format-test.js @@ -4,6 +4,7 @@ import url from "node:url"; import createEsmUtils from "esm-utils"; import getPrettier from "./get-prettier.js"; import getCreateParser from "./get-create-parser.js"; +import getVariantCoverage from "./get-variant-coverage.js"; import getPlugins from "./get-plugins.js"; import compileContract from "./utils/compile-contract.js"; import consistentEndOfLine from "./utils/consistent-end-of-line.js"; @@ -321,24 +322,27 @@ async function runTest({ return; } - if ( - formatOptions.parser === "slang" && - !isAntlrMismatch(filename, formatOptions) - ) { - // Compare with ANTLR's format - const prettier = await getPrettier(); + if (formatOptions.parser === "slang") { const createParser = await getCreateParser(); - const { formatted: antlrOutput } = await prettier.formatWithCursor(code, { - ...formatOptions, - // Since Slang forces us to decide on a compiler version, we need to do the - // same for ANTLR unless it was already given as an option. - compiler: - formatOptions.compiler || - createParser(code, formatOptions).parser.languageVersion, - parser: "antlr", - plugins: await getPlugins(), - }); - expect(antlrOutput).toEqual(formatResult.output); + const variantCoverage = await getVariantCoverage(); + const { parser, parseOutput } = createParser(code, formatOptions); + + // Check coverage + variantCoverage(parseOutput.tree.asNonterminalNode()); + + if (!isAntlrMismatch(filename, formatOptions)) { + // Compare with ANTLR's format + const prettier = await getPrettier(); + const { formatted: antlrOutput } = await prettier.formatWithCursor(code, { + ...formatOptions, + // Since Slang forces us to decide on a compiler version, we need to do the + // same for ANTLR unless it was already given as an option. + compiler: formatOptions.compiler || parser.languageVersion, + parser: "antlr", + plugins: await getPlugins(), + }); + expect(antlrOutput).toEqual(formatResult.output); + } } const isUnstableTest = isUnstable(filename, formatOptions); diff --git a/variant-coverage/ArgumentsDeclaration.ts b/variant-coverage/ArgumentsDeclaration.ts new file mode 100644 index 000000000..1d276bf1d --- /dev/null +++ b/variant-coverage/ArgumentsDeclaration.ts @@ -0,0 +1,12 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of +// `ArgumentsDeclaration`. +export function checkArgumentsDeclarationVariant( + variant: ast.ArgumentsDeclaration['variant'] +): void { + if (variant instanceof ast.PositionalArgumentsDeclaration) return; + if (variant instanceof ast.NamedArgumentsDeclaration) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/ContractMember.ts b/variant-coverage/ContractMember.ts new file mode 100644 index 000000000..101cf9a71 --- /dev/null +++ b/variant-coverage/ContractMember.ts @@ -0,0 +1,22 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `ContractMember`. +export function checkContractMemberVariant( + variant: ast.ContractMember['variant'] +): void { + if (variant instanceof ast.UsingDirective) return; + if (variant instanceof ast.FunctionDefinition) return; + if (variant instanceof ast.ConstructorDefinition) return; + if (variant instanceof ast.ReceiveFunctionDefinition) return; + if (variant instanceof ast.FallbackFunctionDefinition) return; + if (variant instanceof ast.UnnamedFunctionDefinition) return; + if (variant instanceof ast.ModifierDefinition) return; + if (variant instanceof ast.StructDefinition) return; + if (variant instanceof ast.EnumDefinition) return; + if (variant instanceof ast.EventDefinition) return; + if (variant instanceof ast.StateVariableDefinition) return; + if (variant instanceof ast.ErrorDefinition) return; + if (variant instanceof ast.UserDefinedValueTypeDefinition) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/ContractSpecifier.ts b/variant-coverage/ContractSpecifier.ts new file mode 100644 index 000000000..849f9bf00 --- /dev/null +++ b/variant-coverage/ContractSpecifier.ts @@ -0,0 +1,11 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `ContractSpecifier`. +export function checkContractSpecifierVariant( + variant: ast.ContractSpecifier['variant'] +): void { + if (variant instanceof ast.InheritanceSpecifier) return; + if (variant instanceof ast.StorageLayoutSpecifier) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/Expression.ts b/variant-coverage/Expression.ts new file mode 100644 index 000000000..e35eff6c2 --- /dev/null +++ b/variant-coverage/Expression.ts @@ -0,0 +1,38 @@ +import * as ast from '@nomicfoundation/slang/ast'; +import { TerminalNode } from '@nomicfoundation/slang/cst'; + +// This is to ensure that we have handled all variants of `Expression`. +export function checkExpressionVariant( + variant: ast.Expression['variant'] +): void { + if (variant instanceof TerminalNode) return; + if (variant instanceof ast.AssignmentExpression) return; + if (variant instanceof ast.ConditionalExpression) return; + if (variant instanceof ast.OrExpression) return; + if (variant instanceof ast.AndExpression) return; + if (variant instanceof ast.EqualityExpression) return; + if (variant instanceof ast.InequalityExpression) return; + if (variant instanceof ast.BitwiseOrExpression) return; + if (variant instanceof ast.BitwiseXorExpression) return; + if (variant instanceof ast.BitwiseAndExpression) return; + if (variant instanceof ast.ShiftExpression) return; + if (variant instanceof ast.AdditiveExpression) return; + if (variant instanceof ast.MultiplicativeExpression) return; + if (variant instanceof ast.ExponentiationExpression) return; + if (variant instanceof ast.PostfixExpression) return; + if (variant instanceof ast.PrefixExpression) return; + if (variant instanceof ast.FunctionCallExpression) return; + if (variant instanceof ast.CallOptionsExpression) return; + if (variant instanceof ast.MemberAccessExpression) return; + if (variant instanceof ast.IndexAccessExpression) return; + if (variant instanceof ast.NewExpression) return; + if (variant instanceof ast.TupleExpression) return; + if (variant instanceof ast.TypeExpression) return; + if (variant instanceof ast.ArrayExpression) return; + if (variant instanceof ast.HexNumberExpression) return; + if (variant instanceof ast.DecimalNumberExpression) return; + if (variant instanceof ast.StringExpression) return; + if (variant instanceof ast.ElementaryType) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/FallbackFunctionAttribute.ts b/variant-coverage/FallbackFunctionAttribute.ts new file mode 100644 index 000000000..3dfb70ecb --- /dev/null +++ b/variant-coverage/FallbackFunctionAttribute.ts @@ -0,0 +1,14 @@ +import * as ast from '@nomicfoundation/slang/ast'; +import { TerminalNode } from '@nomicfoundation/slang/cst'; + +// This is to ensure that we have handled all variants of +// `FallbackFunctionAttribute`. +export function checkFallbackFunctionAttributeVariant( + variant: ast.FallbackFunctionAttribute['variant'] +): void { + if (variant instanceof TerminalNode) return; + if (variant instanceof ast.ModifierInvocation) return; + if (variant instanceof ast.OverrideSpecifier) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/ForStatementInitialization.ts b/variant-coverage/ForStatementInitialization.ts new file mode 100644 index 000000000..1c87d4f4c --- /dev/null +++ b/variant-coverage/ForStatementInitialization.ts @@ -0,0 +1,15 @@ +import * as ast from '@nomicfoundation/slang/ast'; +import { TerminalNode } from '@nomicfoundation/slang/cst'; + +// This is to ensure that we have handled all variants of +// `ForStatementInitialization`. +export function checkForStatementInitializationVariant( + variant: ast.ForStatementInitialization['variant'] +): void { + if (variant instanceof TerminalNode) return; + if (variant instanceof ast.ExpressionStatement) return; + if (variant instanceof ast.VariableDeclarationStatement) return; + if (variant instanceof ast.TupleDeconstructionStatement) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/FunctionAttribute.ts b/variant-coverage/FunctionAttribute.ts new file mode 100644 index 000000000..ad31eebb1 --- /dev/null +++ b/variant-coverage/FunctionAttribute.ts @@ -0,0 +1,13 @@ +import * as ast from '@nomicfoundation/slang/ast'; +import { TerminalNode } from '@nomicfoundation/slang/cst'; + +// This is to ensure that we have handled all variants of `FunctionAttribute`. +export function checkFunctionAttributeVariant( + variant: ast.FunctionAttribute['variant'] +): void { + if (variant instanceof TerminalNode) return; + if (variant instanceof ast.ModifierInvocation) return; + if (variant instanceof ast.OverrideSpecifier) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/ImportClause.ts b/variant-coverage/ImportClause.ts new file mode 100644 index 000000000..e73c338c3 --- /dev/null +++ b/variant-coverage/ImportClause.ts @@ -0,0 +1,12 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `ImportClause`. +export function checkImportClauseVariant( + variant: ast.ImportClause['variant'] +): void { + if (variant instanceof ast.PathImport) return; + if (variant instanceof ast.NamedImport) return; + if (variant instanceof ast.ImportDeconstruction) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/MappingKeyType.ts b/variant-coverage/MappingKeyType.ts new file mode 100644 index 000000000..dd151e410 --- /dev/null +++ b/variant-coverage/MappingKeyType.ts @@ -0,0 +1,11 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `MappingKeyType`. +export function checkMappingKeyTypeVariant( + variant: ast.MappingKeyType['variant'] +): void { + if (variant instanceof ast.IdentifierPath) return; + if (variant instanceof ast.ElementaryType) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/Pragma.ts b/variant-coverage/Pragma.ts new file mode 100644 index 000000000..ba9fbfd25 --- /dev/null +++ b/variant-coverage/Pragma.ts @@ -0,0 +1,10 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `Pragma`. +export function checkPragmaVariant(variant: ast.Pragma['variant']): void { + if (variant instanceof ast.AbicoderPragma) return; + if (variant instanceof ast.ExperimentalPragma) return; + if (variant instanceof ast.VersionPragma) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/ReceiveFunctionAttribute.ts b/variant-coverage/ReceiveFunctionAttribute.ts new file mode 100644 index 000000000..7ff3090a3 --- /dev/null +++ b/variant-coverage/ReceiveFunctionAttribute.ts @@ -0,0 +1,14 @@ +import * as ast from '@nomicfoundation/slang/ast'; +import { TerminalNode } from '@nomicfoundation/slang/cst'; + +// This is to ensure that we have handled all variants of +// `ReceiveFunctionAttribute`. +export function checkReceiveFunctionAttributeVariant( + variant: ast.ReceiveFunctionAttribute['variant'] +): void { + if (variant instanceof TerminalNode) return; + if (variant instanceof ast.ModifierInvocation) return; + if (variant instanceof ast.OverrideSpecifier) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/SourceUnitMember.ts b/variant-coverage/SourceUnitMember.ts new file mode 100644 index 000000000..57235613d --- /dev/null +++ b/variant-coverage/SourceUnitMember.ts @@ -0,0 +1,22 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `SourceUnitMember`. +export function checkSourceUnitMemberVariant( + variant: ast.SourceUnitMember['variant'] +): void { + if (variant instanceof ast.PragmaDirective) return; + if (variant instanceof ast.ImportDirective) return; + if (variant instanceof ast.ContractDefinition) return; + if (variant instanceof ast.InterfaceDefinition) return; + if (variant instanceof ast.LibraryDefinition) return; + if (variant instanceof ast.StructDefinition) return; + if (variant instanceof ast.EnumDefinition) return; + if (variant instanceof ast.FunctionDefinition) return; + if (variant instanceof ast.ConstantDefinition) return; + if (variant instanceof ast.ErrorDefinition) return; + if (variant instanceof ast.UserDefinedValueTypeDefinition) return; + if (variant instanceof ast.UsingDirective) return; + if (variant instanceof ast.EventDefinition) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/Statement.ts b/variant-coverage/Statement.ts new file mode 100644 index 000000000..ea8be26c9 --- /dev/null +++ b/variant-coverage/Statement.ts @@ -0,0 +1,24 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `Statement`. +export function checkStatementVariant(variant: ast.Statement['variant']): void { + if (variant instanceof ast.ExpressionStatement) return; + if (variant instanceof ast.VariableDeclarationStatement) return; + if (variant instanceof ast.TupleDeconstructionStatement) return; + if (variant instanceof ast.IfStatement) return; + if (variant instanceof ast.ForStatement) return; + if (variant instanceof ast.WhileStatement) return; + if (variant instanceof ast.DoWhileStatement) return; + if (variant instanceof ast.ContinueStatement) return; + if (variant instanceof ast.BreakStatement) return; + if (variant instanceof ast.ReturnStatement) return; + if (variant instanceof ast.ThrowStatement) return; + if (variant instanceof ast.EmitStatement) return; + if (variant instanceof ast.TryStatement) return; + if (variant instanceof ast.RevertStatement) return; + if (variant instanceof ast.AssemblyStatement) return; + if (variant instanceof ast.Block) return; + if (variant instanceof ast.UncheckedBlock) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/StringExpression.ts b/variant-coverage/StringExpression.ts new file mode 100644 index 000000000..0b6cec709 --- /dev/null +++ b/variant-coverage/StringExpression.ts @@ -0,0 +1,14 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `StringExpression`. +export function checkStringExpressionVariant( + variant: ast.StringExpression['variant'] +): void { + if (variant instanceof ast.StringLiteral) return; + if (variant instanceof ast.StringLiterals) return; + if (variant instanceof ast.HexStringLiteral) return; + if (variant instanceof ast.HexStringLiterals) return; + if (variant instanceof ast.UnicodeStringLiterals) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/TupleMember.ts b/variant-coverage/TupleMember.ts new file mode 100644 index 000000000..f01e00b33 --- /dev/null +++ b/variant-coverage/TupleMember.ts @@ -0,0 +1,11 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `TupleMember`. +export function checkTupleMemberVariant( + variant: ast.TupleMember['variant'] +): void { + if (variant instanceof ast.TypedTupleMember) return; + if (variant instanceof ast.UntypedTupleMember) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/TypeName.ts b/variant-coverage/TypeName.ts new file mode 100644 index 000000000..257770e72 --- /dev/null +++ b/variant-coverage/TypeName.ts @@ -0,0 +1,12 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `TypeName`. +export function checkTypeNameVariant(variant: ast.TypeName['variant']): void { + if (variant instanceof ast.ArrayTypeName) return; + if (variant instanceof ast.FunctionType) return; + if (variant instanceof ast.MappingType) return; + if (variant instanceof ast.IdentifierPath) return; + if (variant instanceof ast.ElementaryType) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/UsingClause.ts b/variant-coverage/UsingClause.ts new file mode 100644 index 000000000..f45f0ba78 --- /dev/null +++ b/variant-coverage/UsingClause.ts @@ -0,0 +1,11 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `UsingClause`. +export function checkUsingClauseVariant( + variant: ast.UsingClause['variant'] +): void { + if (variant instanceof ast.IdentifierPath) return; + if (variant instanceof ast.UsingDeconstruction) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/VersionExpression.ts b/variant-coverage/VersionExpression.ts new file mode 100644 index 000000000..4de8195c6 --- /dev/null +++ b/variant-coverage/VersionExpression.ts @@ -0,0 +1,11 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `VersionExpression`. +export function checkVersionExpressionVariant( + variant: ast.VersionExpression['variant'] +): void { + if (variant instanceof ast.VersionRange) return; + if (variant instanceof ast.VersionTerm) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/YulExpression.ts b/variant-coverage/YulExpression.ts new file mode 100644 index 000000000..0a148347e --- /dev/null +++ b/variant-coverage/YulExpression.ts @@ -0,0 +1,12 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `YulExpression`. +export function checkYulExpressionVariant( + variant: ast.YulExpression['variant'] +): void { + if (variant instanceof ast.YulFunctionCallExpression) return; + if (variant instanceof ast.YulLiteral) return; + if (variant instanceof ast.YulPath) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/YulLiteral.ts b/variant-coverage/YulLiteral.ts new file mode 100644 index 000000000..8a5b2c5be --- /dev/null +++ b/variant-coverage/YulLiteral.ts @@ -0,0 +1,13 @@ +import * as ast from '@nomicfoundation/slang/ast'; +import { TerminalNode } from '@nomicfoundation/slang/cst'; + +// This is to ensure that we have handled all variants of `YulLiteral`. +export function checkYulLiteralVariant( + variant: ast.YulLiteral['variant'] +): void { + if (variant instanceof TerminalNode) return; + if (variant instanceof ast.HexStringLiteral) return; + if (variant instanceof ast.StringLiteral) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/YulStatement.ts b/variant-coverage/YulStatement.ts new file mode 100644 index 000000000..e0464f394 --- /dev/null +++ b/variant-coverage/YulStatement.ts @@ -0,0 +1,22 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `YulStatement`. +export function checkYulStatementVariant( + variant: ast.YulStatement['variant'] +): void { + if (variant instanceof ast.YulBlock) return; + if (variant instanceof ast.YulFunctionDefinition) return; + if (variant instanceof ast.YulVariableDeclarationStatement) return; + if (variant instanceof ast.YulVariableAssignmentStatement) return; + if (variant instanceof ast.YulStackAssignmentStatement) return; + if (variant instanceof ast.YulIfStatement) return; + if (variant instanceof ast.YulForStatement) return; + if (variant instanceof ast.YulSwitchStatement) return; + if (variant instanceof ast.YulLeaveStatement) return; + if (variant instanceof ast.YulBreakStatement) return; + if (variant instanceof ast.YulContinueStatement) return; + if (variant instanceof ast.YulLabel) return; + if (variant instanceof ast.YulExpression) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/YulSwitchCase.ts b/variant-coverage/YulSwitchCase.ts new file mode 100644 index 000000000..2bf005cdc --- /dev/null +++ b/variant-coverage/YulSwitchCase.ts @@ -0,0 +1,11 @@ +import * as ast from '@nomicfoundation/slang/ast'; + +// This is to ensure that we have handled all variants of `YulSwitchCase`. +export function checkYulSwitchCaseVariant( + variant: ast.YulSwitchCase['variant'] +): void { + if (variant instanceof ast.YulDefaultCase) return; + if (variant instanceof ast.YulValueCase) return; + /* c8 ignore next 2 */ + const _exhaustiveCheck: never = variant; +} diff --git a/variant-coverage/index.ts b/variant-coverage/index.ts new file mode 100644 index 000000000..8f0bd1b43 --- /dev/null +++ b/variant-coverage/index.ts @@ -0,0 +1,109 @@ +import * as ast from '@nomicfoundation/slang/ast'; +import { NonterminalKind, NonterminalNode } from '@nomicfoundation/slang/cst'; +import { checkArgumentsDeclarationVariant } from './ArgumentsDeclaration.js'; +import { checkContractMemberVariant } from './ContractMember.js'; +import { checkContractSpecifierVariant } from './ContractSpecifier.js'; +import { checkExpressionVariant } from './Expression.js'; +import { checkFallbackFunctionAttributeVariant } from './FallbackFunctionAttribute.js'; +import { checkForStatementInitializationVariant } from './ForStatementInitialization.js'; +import { checkFunctionAttributeVariant } from './FunctionAttribute.js'; +import { checkImportClauseVariant } from './ImportClause.js'; +import { checkMappingKeyTypeVariant } from './MappingKeyType.js'; +import { checkPragmaVariant } from './Pragma.js'; +import { checkReceiveFunctionAttributeVariant } from './ReceiveFunctionAttribute.js'; +import { checkSourceUnitMemberVariant } from './SourceUnitMember.js'; +import { checkStatementVariant } from './Statement.js'; +import { checkStringExpressionVariant } from './StringExpression.js'; +import { checkTupleMemberVariant } from './TupleMember.js'; +import { checkTypeNameVariant } from './TypeName.js'; +import { checkUsingClauseVariant } from './UsingClause.js'; +import { checkVersionExpressionVariant } from './VersionExpression.js'; +import { checkYulExpressionVariant } from './YulExpression.js'; +import { checkYulLiteralVariant } from './YulLiteral.js'; +import { checkYulStatementVariant } from './YulStatement.js'; +import { checkYulSwitchCaseVariant } from './YulSwitchCase.js'; + +export function variantCoverage(cst: NonterminalNode): void { + switch (cst.kind) { + case NonterminalKind.ArgumentsDeclaration: + checkArgumentsDeclarationVariant( + new ast.ArgumentsDeclaration(cst).variant + ); + break; + case NonterminalKind.ContractMember: + checkContractMemberVariant(new ast.ContractMember(cst).variant); + break; + case NonterminalKind.ContractSpecifier: + checkContractSpecifierVariant(new ast.ContractSpecifier(cst).variant); + break; + case NonterminalKind.Expression: + checkExpressionVariant(new ast.Expression(cst).variant); + break; + case NonterminalKind.FallbackFunctionAttribute: + checkFallbackFunctionAttributeVariant( + new ast.FallbackFunctionAttribute(cst).variant + ); + break; + case NonterminalKind.ForStatementInitialization: + checkForStatementInitializationVariant( + new ast.ForStatementInitialization(cst).variant + ); + break; + case NonterminalKind.FunctionAttribute: + checkFunctionAttributeVariant(new ast.FunctionAttribute(cst).variant); + break; + case NonterminalKind.ImportClause: + checkImportClauseVariant(new ast.ImportClause(cst).variant); + break; + case NonterminalKind.MappingKeyType: + checkMappingKeyTypeVariant(new ast.MappingKeyType(cst).variant); + break; + case NonterminalKind.Pragma: + checkPragmaVariant(new ast.Pragma(cst).variant); + break; + case NonterminalKind.ReceiveFunctionAttribute: + checkReceiveFunctionAttributeVariant( + new ast.ReceiveFunctionAttribute(cst).variant + ); + break; + case NonterminalKind.SourceUnitMember: + checkSourceUnitMemberVariant(new ast.SourceUnitMember(cst).variant); + break; + case NonterminalKind.Statement: + checkStatementVariant(new ast.Statement(cst).variant); + break; + case NonterminalKind.StringExpression: + checkStringExpressionVariant(new ast.StringExpression(cst).variant); + break; + case NonterminalKind.TupleMember: + checkTupleMemberVariant(new ast.TupleMember(cst).variant); + break; + case NonterminalKind.TypeName: + checkTypeNameVariant(new ast.TypeName(cst).variant); + break; + case NonterminalKind.UsingClause: + checkUsingClauseVariant(new ast.UsingClause(cst).variant); + break; + case NonterminalKind.VersionExpression: + checkVersionExpressionVariant(new ast.VersionExpression(cst).variant); + break; + case NonterminalKind.YulExpression: + checkYulExpressionVariant(new ast.YulExpression(cst).variant); + break; + case NonterminalKind.YulLiteral: + checkYulLiteralVariant(new ast.YulLiteral(cst).variant); + break; + case NonterminalKind.YulStatement: + checkYulStatementVariant(new ast.YulStatement(cst).variant); + break; + case NonterminalKind.YulSwitchCase: + checkYulSwitchCaseVariant(new ast.YulSwitchCase(cst).variant); + break; + } + + for (const { node } of cst.children()) { + if (node instanceof NonterminalNode) { + variantCoverage(node); + } + } +}