@@ -14,17 +14,27 @@ import {
1414 TerminalKindExtensions ,
1515 TextRange ,
1616} from "@nomicfoundation/slang/cst" ;
17- import { FunctionAttribute } from "@nomicfoundation/slang/ast" ;
17+ import {
18+ FunctionAttribute ,
19+ StateVariableAttribute ,
20+ } from "@nomicfoundation/slang/ast" ;
1821
19- type ModifierKind =
22+ type FunctionModifierKind =
2023 | "visibility"
2124 | "mutability"
2225 | "virtual"
2326 | "override"
2427 | "custom" ;
2528
26- interface ModifierPosition {
27- kind : ModifierKind ;
29+ interface FunctionModifierPosition {
30+ kind : FunctionModifierKind ;
31+ textRange : TextRange ;
32+ }
33+
34+ type StateVarModifierKind = "visibility" | "mutability" ;
35+
36+ interface StateVarModifierPosition {
37+ kind : StateVarModifierKind ;
2838 textRange : TextRange ;
2939}
3040
@@ -44,6 +54,27 @@ class SortModifiersRule implements RuleWithoutConfig {
4454
4555 const cursor = file . createTreeCursor ( ) ;
4656
57+ const functionModifiersResults = this . _checkFunctionModifiers (
58+ file ,
59+ cursor . clone ( ) ,
60+ ) ;
61+ results . push ( ...functionModifiersResults ) ;
62+
63+ const stateVarModifiersResults = this . _checkStateVarModifiers (
64+ file ,
65+ cursor . clone ( ) ,
66+ ) ;
67+ results . push ( ...stateVarModifiersResults ) ;
68+
69+ return results ;
70+ }
71+
72+ private _checkFunctionModifiers (
73+ file : SlangFile ,
74+ cursor : Cursor ,
75+ ) : LintResult [ ] {
76+ const results : LintResult [ ] = [ ] ;
77+
4778 while (
4879 cursor . goToNextNonterminalWithKinds ( [
4980 NonterminalKind . FunctionDefinition ,
@@ -61,7 +92,7 @@ class SortModifiersRule implements RuleWithoutConfig {
6192 }
6293 const functionCursor = cursor . spawn ( ) ;
6394
64- const modifiers : ModifierPosition [ ] = [ ] ;
95+ const modifiers : FunctionModifierPosition [ ] = [ ] ;
6596
6697 while (
6798 functionCursor . goToNextNonterminalWithKind (
@@ -147,27 +178,119 @@ class SortModifiersRule implements RuleWithoutConfig {
147178 } ,
148179 ] ;
149180
150- for ( let i = 0 ; i < modifiersIndices . length - 1 ; i ++ ) {
151- for ( let k = i + 1 ; k < modifiersIndices . length ; k ++ ) {
152- const first = modifiersIndices [ i ] ;
153- const second = modifiersIndices [ k ] ;
154-
155- if (
156- first . index !== - 1 &&
157- second . index !== - 1 &&
158- first . index > second . index
159- ) {
160- return [
161- this . _buildError (
162- file ,
163- first . kind ,
164- second . kind ,
165- modifiers [ second . index ] . textRange ,
166- ) ,
167- ] ;
181+ results . push (
182+ ...this . _checkModifiersOrder ( file , modifiers , modifiersIndices ) ,
183+ ) ;
184+ }
185+
186+ return results ;
187+ }
188+
189+ private _checkStateVarModifiers (
190+ file : SlangFile ,
191+ cursor : Cursor ,
192+ ) : LintResult [ ] {
193+ const results : LintResult [ ] = [ ] ;
194+
195+ while (
196+ cursor . goToNextNonterminalWithKind (
197+ NonterminalKind . StateVariableDefinition ,
198+ )
199+ ) {
200+ const stateVarTextRangeCursor = cursor . spawn ( ) ;
201+ while (
202+ stateVarTextRangeCursor . goToNextTerminal ( ) &&
203+ stateVarTextRangeCursor . node . isTerminalNode ( ) &&
204+ TerminalKindExtensions . isTrivia ( stateVarTextRangeCursor . node . kind )
205+ ) {
206+ // skip trivia nodes
207+ }
208+ const stateVarCursor = cursor . spawn ( ) ;
209+
210+ const modifiers : StateVarModifierPosition [ ] = [ ] ;
211+
212+ while (
213+ stateVarCursor . goToNextNonterminalWithKind (
214+ NonterminalKind . StateVariableAttribute ,
215+ )
216+ ) {
217+ assertNonterminalNode ( stateVarCursor . node ) ;
218+ const variant = new StateVariableAttribute ( stateVarCursor . node ) . variant ;
219+
220+ if ( "kind" in variant ) {
221+ // we know it's a terminal node
222+ assertTerminalNode ( variant ) ;
223+ switch ( variant . kind ) {
224+ case TerminalKind . InternalKeyword :
225+ case TerminalKind . PublicKeyword :
226+ case TerminalKind . PrivateKeyword :
227+ ignoreTrivia ( stateVarCursor ) ;
228+ modifiers . push ( {
229+ kind : "visibility" ,
230+ textRange : stateVarCursor . textRange ,
231+ } ) ;
232+ break ;
233+ case TerminalKind . ConstantKeyword :
234+ case TerminalKind . ImmutableKeyword :
235+ case TerminalKind . TransientKeyword :
236+ ignoreTrivia ( stateVarCursor ) ;
237+ modifiers . push ( {
238+ kind : "mutability" ,
239+ textRange : stateVarCursor . textRange ,
240+ } ) ;
241+ break ;
168242 }
169243 }
170244 }
245+
246+ const modifiersIndices = [
247+ {
248+ kind : "visibility" ,
249+ index : modifiers . findIndex ( ( m ) => m . kind === "visibility" ) ,
250+ } ,
251+ {
252+ kind : "mutability" ,
253+ index : modifiers . findIndex ( ( m ) => m . kind === "mutability" ) ,
254+ } ,
255+ ] ;
256+
257+ results . push (
258+ ...this . _checkModifiersOrder ( file , modifiers , modifiersIndices ) ,
259+ ) ;
260+ }
261+
262+ return results ;
263+ }
264+
265+ private _checkModifiersOrder (
266+ file : SlangFile ,
267+ modifiers :
268+ | Array < FunctionModifierPosition >
269+ | Array < StateVarModifierPosition > ,
270+ modifiersIndices : Array < { kind : string ; index : number } > ,
271+ ) : LintResult [ ] {
272+ const results : LintResult [ ] = [ ] ;
273+
274+ for ( let i = 0 ; i < modifiersIndices . length - 1 ; i ++ ) {
275+ for ( let k = i + 1 ; k < modifiersIndices . length ; k ++ ) {
276+ const first = modifiersIndices [ i ] ;
277+ const second = modifiersIndices [ k ] ;
278+
279+ if (
280+ first . index !== - 1 &&
281+ second . index !== - 1 &&
282+ first . index > second . index
283+ ) {
284+ return [
285+ this . _buildError (
286+ file ,
287+ first . kind ,
288+ second . kind ,
289+ modifiers [ second . index ] . textRange ,
290+ ) ,
291+ ] ;
292+ }
293+ }
171294 }
172295
173296 return results ;
0 commit comments