UNPKG

@slippy-lint/slippy

Version:

A simple but powerful linter for Solidity

99 lines (85 loc) 2.79 kB
import { StateVariableDefinition } from "@nomicfoundation/slang/ast"; import { hasDefaultVisibility } from "../slang/state-variables.js"; import { Diagnostic, RuleContext, RuleWithoutConfig, RuleDefinitionWithoutConfig, } from "./types.js"; import { assertNonterminalNode, Cursor, NonterminalKind, TerminalKind, TerminalKindExtensions, } from "@nomicfoundation/slang/cst"; export const NoDefaultVisibility: RuleDefinitionWithoutConfig = { name: "no-default-visibility", recommended: true, create: function () { return new NoDefaultVisibilityRule(this.name); }, }; class NoDefaultVisibilityRule implements RuleWithoutConfig { public constructor(public name: string) {} public run({ content, file }: RuleContext): Diagnostic[] { const diagnostics: Diagnostic[] = []; const cursor = file.createTreeCursor(); while ( cursor.goToNextNonterminalWithKind( NonterminalKind.StateVariableDefinition, ) ) { assertNonterminalNode(cursor.node); const stateVariableDefinition = new StateVariableDefinition(cursor.node); const defaultVisibility = hasDefaultVisibility(stateVariableDefinition); if (defaultVisibility) { const nameCursor = getNameCursor( cursor.spawn(), stateVariableDefinition, ); const name = nameCursor.node.unparse(); const firstTerminalCursor = cursor.spawn(); while ( firstTerminalCursor.goToNextTerminal() && firstTerminalCursor.node.isTerminalNode() && TerminalKindExtensions.isTrivia(firstTerminalCursor.node.kind) ) { // ignore trivia } const insertionPoint = nameCursor.textRange.start.utf16; const charBeforeInsertionPoint = content[insertionPoint - 1]; const hasNonWhitespaceBefore = charBeforeInsertionPoint !== undefined && charBeforeInsertionPoint.match(/\S/) !== null; diagnostics.push({ rule: this.name, sourceId: file.id, line: firstTerminalCursor.textRange.start.line, column: firstTerminalCursor.textRange.start.column, message: `State variable '${name}' has default visibility`, fix: [ { range: [insertionPoint, insertionPoint], replacement: hasNonWhitespaceBefore ? " public " : "public ", }, ], }); } } return diagnostics; } } function getNameCursor( cursor: Cursor, stateVariableDefinition: StateVariableDefinition, ): Cursor { const nameId = stateVariableDefinition.name.id; while ( cursor.node.id !== nameId && cursor.goToNextTerminalWithKind(TerminalKind.Identifier) ) { // iterate to the name } return cursor; }