ESC
Type to search...
S
Soli Docs

Linting

Built-in static analysis to catch style issues and code smells without executing your code.

Usage

# Lint all .sl files in current directory (recursive)
soli lint

# Lint a specific directory
soli lint src/

# Lint a single file
soli lint app/main.sl

Exit codes

0 — no issues found. 1 — one or more issues found.

Output Format

Each issue is reported on a single line with the file path, line, column, rule, and message:

app/main.sl:12:5 - [naming/snake-case] variable 'myVar' should use snake_case
app/main.sl:30:9 - [smell/unreachable-code] unreachable code after return statement

2 issue(s)
found in 1 file(s)

Rules

Naming

naming/snake-case

Variables, functions, methods, and parameters should use snake_case.

# Bad
let myVar = 10
def processData end

# Good
let my_var = 10
def process_data end
naming/pascal-case

Classes and interfaces should use PascalCase.

# Bad
class my_class end
interface data_store end

# Good
class MyClass end
interface DataStore end

Style

style/empty-block

Blocks should not be empty. Add a comment or remove the block.

style/line-length

Lines should not exceed 120 characters.

Code Smells

smell/unreachable-code

Code after a return statement is unreachable and will never execute.

def example
  return 42
  print("never reached");  # Warning: unreachable code
end
smell/empty-catch

Catch blocks should not be empty. Silently swallowing errors hides bugs.

# Bad - error silently ignored
try
  risky()catch e
end

# Good - at least log the error
try
  risky()catch e
  print("Error: " + str(e))
end
smell/deep-nesting

Nesting depth should not exceed 4 levels. Consider extracting logic into separate functions.

smell/duplicate-methods

A class should not have two methods with the same name.

smell/dangerous-server-builtin

Calls to db_query_raw, Trusted.*, System.shell / System.shell_sync, or backtick command substitution from app/controllers/, app/middleware/, or app/views/. These primitives are powerful but become injection / traversal sinks when fed request-controlled data. The diagnostic spells out the safe alternative for each:

  • db_query_raw → parameterised @sdbql{ ... #{value} ... } block, or Model.where("x = #{v}", { "v": v }).
  • Trusted.* → jailed File.* (read/write/exists), which keeps every operation under the app root.
  • System.shell / backticks → System.run(["prog", "arg1", ...]) with an argv array, which never invokes a shell.

Models, migrations, and tests are out of scope — those layers legitimately use these APIs against operator-controlled data.

Suppressing Warnings

When a warning is a known false-positive or an intentional exception, suppress it inline with a directive comment.

Single-line forms

disable-next-line covers the line below; disable-line covers the same line.

# soli-lint-disable-next-line smell/dangerous-server-builtin
if Trusted.is_dir(wt_path)
  ...
end

Trusted.read(p)  # soli-lint-disable-line smell/dangerous-server-builtin

Block forms

disable / enable toggle a rule for a region. Useful when several adjacent lines are intentional exceptions.

# soli-lint-disable smell/dangerous-server-builtin
exists = Trusted.is_dir(path)
data   = Trusted.read(path)
# soli-lint-enable smell/dangerous-server-builtin
  • Omit the rule name to suppress every rule (e.g. # soli-lint-disable). Pass a comma-separated list to scope to multiple rules.
  • An enable for a specific rule re-enables only that rule, even if the prior disable was a blanket one.
  • A block disable with no matching enable runs to the end of the file.
  • Prefer naming the exact rule so unrelated warnings still surface.

Editor Integration

The VS Code / Cursor extension provides full Language Server Protocol (LSP) support with real-time linting, hover documentation, autocomplete, and more.

Features

  • Real-time linting — warnings and errors displayed inline
  • Hover information — documentation for functions, classes, and builtins
  • Autocomplete — suggestions for keywords, types, and symbols
  • Go to definition — jump to symbol definitions
  • Find references — locate all uses of a symbol

Installation

cd editors/vscode
vsce package
# Install the generated .vsix file in Cursor or VS Code

Settings

  • soli.lsp.enable — Enable/disable LSP server (default: true)
  • soli.lsp.executablePath — Path to the soli binary (default: "soli")
  • soli.lint.enable — Enable/disable linting (default: true)
  • soli.lint.onSave — Run linter on file save (default: true)

Manual LSP Setup

For editors that support custom LSP servers directly (Neovim, Emacs, etc.):

require('lspconfig').soli.setup({
 cmd = {"soli", "lsp"},
 filetypes = {"soli"},
 root_dir = lspconfig.util.root_pattern("soli.toml", ".git"),
})

Learn More

For full editor setup instructions and all available LSP features, see the Editor Integration guide.