Skip to content

Commit 91cbfc5

Browse files
authored
Merge pull request #1123 from prettier-solidity/slang-1.1.0
Slang 1.1.0
2 parents f20855d + f1728fb commit 91cbfc5

16 files changed

+644
-451
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Nomic Foundation has put a lot of effort in providing a set of compiler APIs tha
1616

1717
Since v2.0.0 this package will ship with the Slang parser and this change must be implemented in existing configurations by replacing `parser: 'solidity-parse'` with `parser: 'slang-solidity'`.
1818

19+
The `antlr4` parser (`solidity-parse`) is still supported for the time being and will trigger a deprecation warning, since Slang gives us a much more powerful tool, is faster, allowed us to fully transition into typescript (minimizing the introduction of mismatching type bugs), and allows prettier to format the code in a much more decoupled way and position comments with a greater precision.
20+
1921
## Installation and usage
2022

2123
### Using in NodeJS

package-lock.json

Lines changed: 354 additions & 413 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@
8484
},
8585
"devDependencies": {
8686
"@babel/code-frame": "^7.26.2",
87+
"@eslint/eslintrc": "^3.3.1",
88+
"@eslint/js": "^9.25.1",
8789
"@types/jest": "^29.5.14",
8890
"@types/semver": "^7.7.0",
8991
"@typescript-eslint/eslint-plugin": "^8.31.0",
@@ -93,6 +95,7 @@
9395
"eslint": "^9.25.1",
9496
"eslint-config-prettier": "^10.1.2",
9597
"esm-utils": "^4.3.0",
98+
"globals": "^16.0.0",
9699
"jest": "^29.7.0",
97100
"jest-light-runner": "^0.7.8",
98101
"jest-snapshot-serializer-ansi": "^2.2.1",
@@ -108,7 +111,7 @@
108111
"webpack-cli": "^6.0.1"
109112
},
110113
"dependencies": {
111-
"@nomicfoundation/slang": "1.0.0",
114+
"@nomicfoundation/slang": "1.1.0",
112115
"@solidity-parser/parser": "^0.20.1",
113116
"semver": "^7.7.1"
114117
},

src/slang-comments/handlers/handle-contract-definition-comments.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ export default function handleContractDefinitionComments({
2323
locEnd(comment)
2424
);
2525

26-
// Everything before the InheritanceSpecifier is pushed onto the beginning of
26+
// Everything before the ContractSpecifiers is pushed onto the beginning of
2727
// the ContractDefinition.
2828
if (
29-
followingNode?.kind === NonterminalKind.InheritanceSpecifier ||
29+
followingNode?.kind === NonterminalKind.ContractSpecifiers ||
3030
(followingNode?.kind === NonterminalKind.ContractMembers &&
3131
nextCharacter !== '{')
3232
) {
@@ -42,17 +42,25 @@ export default function handleContractDefinitionComments({
4242

4343
// The last comments before the body.
4444
if (nextCharacter === '{') {
45-
// If there's an InheritanceSpecifier, the comment is appended to the last
46-
// InheritanceType.
47-
if (
48-
precedingNode?.kind === NonterminalKind.InheritanceSpecifier &&
49-
precedingNode.types.items.length > 0
50-
) {
51-
addTrailingComment(
52-
precedingNode.types.items[precedingNode.types.items.length - 1],
53-
comment
54-
);
55-
return true;
45+
if (precedingNode?.kind === NonterminalKind.ContractSpecifiers) {
46+
if (precedingNode.items.length === 0) {
47+
addTrailingComment(precedingNode, comment);
48+
return true;
49+
}
50+
const lastContractSpecifier =
51+
precedingNode.items[precedingNode.items.length - 1].variant;
52+
// If the last ContractSpecifier's an InheritanceSpecifier, the comment
53+
// is appended to the last InheritanceType.
54+
if (lastContractSpecifier.kind === NonterminalKind.InheritanceSpecifier) {
55+
addCollectionNodeLastComment(lastContractSpecifier.types, comment);
56+
return true;
57+
}
58+
if (
59+
lastContractSpecifier.kind === NonterminalKind.StorageLayoutSpecifier
60+
) {
61+
addTrailingComment(lastContractSpecifier.expression, comment);
62+
return true;
63+
}
5664
}
5765
}
5866

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { NonterminalKind } from '@nomicfoundation/slang/cst';
2+
import { util } from 'prettier';
3+
import addCollectionNodeLastComment from './add-collection-node-last-comment.js';
4+
5+
import type { HandlerParams } from './types.d.ts';
6+
7+
const { addTrailingComment } = util;
8+
9+
export default function handleContractSpecifiersComments({
10+
precedingNode,
11+
enclosingNode,
12+
comment
13+
}: HandlerParams): boolean {
14+
if (enclosingNode?.kind !== NonterminalKind.ContractSpecifiers) {
15+
return false;
16+
}
17+
18+
if (
19+
precedingNode &&
20+
precedingNode.kind === NonterminalKind.ContractSpecifier
21+
) {
22+
if (precedingNode.variant.kind === NonterminalKind.InheritanceSpecifier) {
23+
addCollectionNodeLastComment(precedingNode.variant.types, comment);
24+
return true;
25+
}
26+
if (precedingNode.variant.kind === NonterminalKind.StorageLayoutSpecifier) {
27+
addTrailingComment(precedingNode.variant.expression, comment);
28+
return true;
29+
}
30+
}
31+
32+
return false;
33+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { NonterminalKind } from '@nomicfoundation/slang/cst';
2+
import { util } from 'prettier';
3+
4+
import type { HandlerParams } from './types.d.ts';
5+
6+
const { addLeadingComment } = util;
7+
8+
export default function handleStorageLayoutSpecifierComments({
9+
enclosingNode,
10+
followingNode,
11+
comment
12+
}: HandlerParams): boolean {
13+
if (enclosingNode?.kind !== NonterminalKind.StorageLayoutSpecifier) {
14+
return false;
15+
}
16+
17+
if (followingNode?.kind === NonterminalKind.Expression) {
18+
addLeadingComment(followingNode, comment);
19+
return true;
20+
}
21+
22+
return false;
23+
}

src/slang-comments/handlers/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
import handleBlockComments from './handle-block-comments.js';
22
import handleContractDefinitionComments from './handle-contract-definition-comments.js';
3+
import handleContractSpecifiersComments from './handle-contract-specifiers-comments.js';
34
import handleElseBranchComments from './handle-else-branch-comments.js';
45
import handleIfStatementComments from './handle-if-statement-comments.js';
56
import handleInterfaceDefinitionComments from './handle-interface-definition-comments.js';
67
import handleLibraryDefinitionComments from './handle-library-definition-comments.js';
78
import handleModifierInvocationComments from './handle-modifier-invocation-comments.js';
89
import handleParametersDeclarationComments from './handle-parameters-declaration-comments.js';
910
import handlePositionalArgumentsDeclarationComments from './handle-positional-arguments-declaration-comments.js';
11+
import handleStorageLayoutSpecifierComments from './handle-storage-layout-specifier-comments.js';
1012
import handleWhileStatementComments from './handle-while-statement-comments.js';
1113
import handleYulBlockComments from './handle-yul-block-comments.js';
1214

1315
export default [
1416
handleBlockComments,
1517
handleContractDefinitionComments,
18+
handleContractSpecifiersComments,
1619
handleElseBranchComments,
1720
handleIfStatementComments,
1821
handleInterfaceDefinitionComments,
1922
handleLibraryDefinitionComments,
2023
handleModifierInvocationComments,
2124
handleParametersDeclarationComments,
2225
handlePositionalArgumentsDeclarationComments,
26+
handleStorageLayoutSpecifierComments,
2327
handleWhileStatementComments,
2428
handleYulBlockComments
2529
];

src/slang-nodes/ContractDefinition.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { coerce, satisfies } from 'semver';
33
import { NonterminalKind } from '@nomicfoundation/slang/cst';
44
import { getNodeMetadata, updateMetadata } from '../slang-utils/metadata.js';
55
import { Identifier } from './Identifier.js';
6-
import { InheritanceSpecifier } from './InheritanceSpecifier.js';
6+
import { ContractSpecifiers } from './ContractSpecifiers.js';
77
import { ContractMembers } from './ContractMembers.js';
88

99
import type * as ast from '@nomicfoundation/slang/ast';
@@ -24,7 +24,7 @@ export class ContractDefinition implements SlangNode {
2424

2525
name: Identifier;
2626

27-
inheritance?: InheritanceSpecifier;
27+
specifiers: ContractSpecifiers;
2828

2929
members: ContractMembers;
3030

@@ -33,12 +33,10 @@ export class ContractDefinition implements SlangNode {
3333

3434
this.abstractKeyword = ast.abstractKeyword?.unparse();
3535
this.name = new Identifier(ast.name);
36-
if (ast.inheritance) {
37-
this.inheritance = new InheritanceSpecifier(ast.inheritance, options);
38-
}
36+
this.specifiers = new ContractSpecifiers(ast.specifiers, options);
3937
this.members = new ContractMembers(ast.members, options);
4038

41-
metadata = updateMetadata(metadata, [this.inheritance, this.members]);
39+
metadata = updateMetadata(metadata, [this.specifiers, this.members]);
4240

4341
this.comments = metadata.comments;
4442
this.loc = metadata.loc;
@@ -68,7 +66,8 @@ export class ContractDefinition implements SlangNode {
6866
this.abstractKeyword ? 'abstract ' : '',
6967
'contract ',
7068
path.call(print, 'name'),
71-
this.inheritance ? [' ', path.call(print, 'inheritance')] : line,
69+
path.call(print, 'specifiers'),
70+
this.specifiers.items.length > 0 ? '' : line,
7271
'{'
7372
]),
7473
path.call(print, 'members'),

src/slang-nodes/ContractSpecifier.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { NonterminalKind } from '@nomicfoundation/slang/cst';
2+
import { getNodeMetadata, updateMetadata } from '../slang-utils/metadata.js';
3+
import { InheritanceSpecifier } from './InheritanceSpecifier.js';
4+
import { StorageLayoutSpecifier } from './StorageLayoutSpecifier.js';
5+
6+
import type * as ast from '@nomicfoundation/slang/ast';
7+
import type { AstPath, Doc, ParserOptions } from 'prettier';
8+
import type { AstNode } from './types.js';
9+
import type { PrintFunction, SlangNode } from '../types.js';
10+
11+
export class ContractSpecifier implements SlangNode {
12+
readonly kind = NonterminalKind.ContractSpecifier;
13+
14+
comments;
15+
16+
loc;
17+
18+
variant: InheritanceSpecifier | StorageLayoutSpecifier;
19+
20+
constructor(ast: ast.ContractSpecifier, options: ParserOptions<AstNode>) {
21+
let metadata = getNodeMetadata(ast);
22+
23+
switch (ast.variant.cst.kind) {
24+
case NonterminalKind.InheritanceSpecifier:
25+
this.variant = new InheritanceSpecifier(
26+
ast.variant as ast.InheritanceSpecifier,
27+
options
28+
);
29+
break;
30+
case NonterminalKind.StorageLayoutSpecifier:
31+
this.variant = new StorageLayoutSpecifier(
32+
ast.variant as ast.StorageLayoutSpecifier,
33+
options
34+
);
35+
break;
36+
default:
37+
throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`);
38+
}
39+
metadata = updateMetadata(metadata, [this.variant]);
40+
41+
this.comments = metadata.comments;
42+
this.loc = metadata.loc;
43+
}
44+
45+
print(path: AstPath<ContractSpecifier>, print: PrintFunction): Doc {
46+
return path.call(print, 'variant');
47+
}
48+
}

src/slang-nodes/ContractSpecifiers.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { doc } from 'prettier';
2+
import { NonterminalKind } from '@nomicfoundation/slang/cst';
3+
import { sortContractSpecifiers } from '../slang-utils/sort-contract-specifiers.js';
4+
import { printSeparatedList } from '../slang-printers/print-separated-list.js';
5+
import { getNodeMetadata, updateMetadata } from '../slang-utils/metadata.js';
6+
import { ContractSpecifier } from './ContractSpecifier.js';
7+
8+
import type * as ast from '@nomicfoundation/slang/ast';
9+
import type { AstPath, Doc, ParserOptions } from 'prettier';
10+
import type { AstNode } from './types.js';
11+
import type { PrintFunction, SlangNode } from '../types.js';
12+
13+
const { group, ifBreak, line, softline } = doc.builders;
14+
15+
export class ContractSpecifiers implements SlangNode {
16+
readonly kind = NonterminalKind.ContractSpecifiers;
17+
18+
comments;
19+
20+
loc;
21+
22+
items: ContractSpecifier[];
23+
24+
constructor(ast: ast.ContractSpecifiers, options: ParserOptions<AstNode>) {
25+
let metadata = getNodeMetadata(ast, true);
26+
27+
this.items = ast.items.map((item) => new ContractSpecifier(item, options));
28+
29+
metadata = updateMetadata(metadata, [this.items]);
30+
31+
this.comments = metadata.comments;
32+
this.loc = metadata.loc;
33+
34+
this.items = this.items.sort(sortContractSpecifiers);
35+
}
36+
37+
print(path: AstPath<ContractSpecifiers>, print: PrintFunction): Doc {
38+
if (this.items.length === 0) return '';
39+
40+
const [specifier1, specifier2] = path.map(print, 'items');
41+
if (typeof specifier2 === 'undefined') return [' ', specifier1];
42+
43+
const groupId = Symbol('Slang.ContractSpecifiers.inheritance');
44+
return printSeparatedList(
45+
[group(specifier1, { id: groupId }), specifier2],
46+
{
47+
firstSeparator: line,
48+
separator: ifBreak('', softline, { groupId })
49+
}
50+
);
51+
}
52+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { doc } from 'prettier';
2+
import { NonterminalKind } from '@nomicfoundation/slang/cst';
3+
import { printSeparatedItem } from '../slang-printers/print-separated-item.js';
4+
import { getNodeMetadata, updateMetadata } from '../slang-utils/metadata.js';
5+
import { Expression } from './Expression.js';
6+
7+
import type * as ast from '@nomicfoundation/slang/ast';
8+
import type { AstPath, Doc, ParserOptions } from 'prettier';
9+
import type { AstNode } from './types.js';
10+
import type { PrintFunction, SlangNode } from '../types.js';
11+
12+
const { line } = doc.builders;
13+
14+
export class StorageLayoutSpecifier implements SlangNode {
15+
readonly kind = NonterminalKind.StorageLayoutSpecifier;
16+
17+
comments;
18+
19+
loc;
20+
21+
expression: Expression;
22+
23+
constructor(
24+
ast: ast.StorageLayoutSpecifier,
25+
options: ParserOptions<AstNode>
26+
) {
27+
let metadata = getNodeMetadata(ast);
28+
29+
this.expression = new Expression(ast.expression, options);
30+
31+
metadata = updateMetadata(metadata, [this.expression]);
32+
33+
this.comments = metadata.comments;
34+
this.loc = metadata.loc;
35+
}
36+
37+
print(path: AstPath<StorageLayoutSpecifier>, print: PrintFunction): Doc {
38+
return [
39+
'layout at',
40+
printSeparatedItem(path.call(print, 'expression'), {
41+
firstSeparator: line
42+
})
43+
];
44+
}
45+
}

src/slang-nodes/types.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import type { ContinueStatement } from './ContinueStatement.ts';
2929
import type { ContractDefinition } from './ContractDefinition.ts';
3030
import type { ContractMember } from './ContractMember.ts';
3131
import type { ContractMembers } from './ContractMembers.ts';
32+
import type { ContractSpecifier } from './ContractSpecifier.ts';
33+
import type { ContractSpecifiers } from './ContractSpecifiers.ts';
3234
import type { DecimalNumberExpression } from './DecimalNumberExpression.ts';
3335
import type { DoWhileStatement } from './DoWhileStatement.ts';
3436
import type { ElementaryType } from './ElementaryType.ts';
@@ -139,6 +141,7 @@ import type { StateVariableAttribute } from './StateVariableAttribute.ts';
139141
import type { StateVariableAttributes } from './StateVariableAttributes.ts';
140142
import type { StateVariableDefinition } from './StateVariableDefinition.ts';
141143
import type { StateVariableDefinitionValue } from './StateVariableDefinitionValue.ts';
144+
import type { StorageLayoutSpecifier } from './StorageLayoutSpecifier.ts';
142145
import type { StorageLocation } from './StorageLocation.ts';
143146
import type { StringExpression } from './StringExpression.ts';
144147
import type { StringLiteral } from './StringLiteral.ts';
@@ -257,6 +260,7 @@ export type StrictAstNode =
257260
| ContractDefinition
258261
| InheritanceSpecifier
259262
| InheritanceType
263+
| StorageLayoutSpecifier
260264
| InterfaceDefinition
261265
| LibraryDefinition
262266
| StructDefinition
@@ -411,6 +415,8 @@ export type StrictAstNode =
411415
| YulLiteral
412416
| SourceUnitMembers
413417
| VersionExpressionSet
418+
| ContractSpecifier
419+
| ContractSpecifiers
414420
| ContractMembers
415421
| InterfaceMembers
416422
| LibraryMembers

0 commit comments

Comments
 (0)