lint

package
v0.0.0-...-e6c4605 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 6, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package lint provides a unified SQL and project linting framework.

Architecture

The lint package follows a modular architecture with three layers:

  1. Root package (pkg/lint/): Contains shared contracts, interfaces, and the unified registry
  2. SQL subsystem (pkg/lint/sql/): SQL statement analysis with dialect-aware rules
  3. Project subsystem (pkg/lint/project/): Project-level analysis (DAG, naming, architecture)

Rule Registration

Rules are automatically registered via init() functions when their packages are imported:

// Import SQL rules
import _ "github.com/leapstack-labs/leapsql/pkg/lint/sql/rules"

// Import project rules
import _ "github.com/leapstack-labs/leapsql/pkg/lint/project/rules"

Rule Categories

SQL Rules (statement-level):

  • AL (Aliasing): Rules about alias usage and naming
  • AM (Ambiguous): Rules about ambiguous SQL constructs
  • CV (Convention): Rules about SQL coding conventions
  • RF (References): Rules about column and table references
  • ST (Structure): Rules about SQL query structure

Project Rules (architecture-level):

  • PL (Lineage): Rules about data lineage and dependencies
  • PM (Modeling): Rules about model structure and organization
  • PS (Structure): Rules about project structure and naming

Using the Registry

Query all registered rules:

rules := lint.AllRules()
sqlRules := lint.GetAllSQLRules()
projectRules := lint.GetAllProjectRules()

Query rules by ID, group, or dialect:

rule, ok := lint.GetRuleByID("AM01")
sqlRules := lint.GetSQLRulesByDialect("postgres")
groupRules := lint.GetSQLRulesByGroup("ambiguous")

Configuration

Use Config to control which rules are enabled and their severity:

config := lint.NewConfig()
config.Disable("AM01")
config.SetSeverity("CV05", core.SeverityError)
config.SetRuleOptions("AL06", map[string]any{"min_length": 3})

Creating Custom Rules

For SQL rules, implement the SQLRule interface or use RuleDef:

var MyRule = sql.RuleDef{
	ID:          "MY01",
	Name:        "my.custom_rule",
	Group:       "custom",
	Description: "My custom rule description",
	Severity:    core.SeverityWarning,
	Check:       checkMyRule,
}

func init() {
	sql.Register(MyRule)
}

For project rules, implement the ProjectRule interface in the project package.

Index

Constants

View Source
const DefaultDocsBaseURL = "https://leapsql.dev/docs/rules"

DefaultDocsBaseURL is the hosted documentation site.

Variables

View Source
var DocsBaseURL = DefaultDocsBaseURL

DocsBaseURL can be overridden via config for local/offline mode.

Functions

func AllRules

func AllRules() []core.RuleInfo

AllRules returns metadata for all registered rules (both SQL and project).

func BuildDocURL

func BuildDocURL(ruleID string) string

BuildDocURL constructs a documentation URL for a rule.

func Clear

func Clear()

Clear removes all rules from the registry. Used for testing.

func CountProjectRules

func CountProjectRules() int

CountProjectRules returns the number of registered project rules.

func CountSQLRules

func CountSQLRules() int

CountSQLRules returns the number of registered SQL rules.

func GetBoolOption

func GetBoolOption(opts map[string]any, key string, defaultVal bool) bool

GetBoolOption extracts a bool option.

func GetIntOption

func GetIntOption(opts map[string]any, key string, defaultVal int) int

GetIntOption extracts an int option, handling float64 from JSON.

func GetOption

func GetOption[T any](opts map[string]any, key string, defaultVal T) T

GetOption extracts a typed option with a default value.

func GetRuleInfo

func GetRuleInfo(r Rule) core.RuleInfo

GetRuleInfo extracts metadata from a Rule for documentation/tooling.

func GetStringOption

func GetStringOption(opts map[string]any, key string, defaultVal string) string

GetStringOption extracts a string option.

func GetStringSliceOption

func GetStringSliceOption(opts map[string]any, key string, defaultVal []string) []string

GetStringSliceOption extracts a string slice option.

func RegisterProjectRule

func RegisterProjectRule(rule ProjectRule)

RegisterProjectRule adds a project rule to the registry. Call this from init() functions in rule packages.

func RegisterSQLRule

func RegisterSQLRule(rule SQLRule)

RegisterSQLRule adds an SQL rule to the registry. Call this from init() functions in rule packages.

func ResetDocsBaseURL

func ResetDocsBaseURL()

ResetDocsBaseURL resets to the default documentation URL.

func SetDocsBaseURL

func SetDocsBaseURL(url string)

SetDocsBaseURL overrides the default documentation base URL. Useful for offline mode or custom documentation sites.

Types

type Analyzer

type Analyzer struct {
	// contains filtered or unexported fields
}

Analyzer runs lint rules against parsed SQL.

func NewAnalyzer

func NewAnalyzer(config *Config) *Analyzer

NewAnalyzer creates a new analyzer with optional configuration.

func NewAnalyzerWithRegistry

func NewAnalyzerWithRegistry(config *Config, dialect string) *Analyzer

NewAnalyzerWithRegistry creates an analyzer that uses registry rules filtered by dialect.

func (*Analyzer) Analyze

func (a *Analyzer) Analyze(stmt any, dialect DialectInfo) []Diagnostic

Analyze runs all rules from the dialect against the statement. The stmt parameter should be *core.SelectStmt.

func (*Analyzer) AnalyzeMultiple

func (a *Analyzer) AnalyzeMultiple(stmts []any, dialect DialectInfo) []Diagnostic

AnalyzeMultiple runs analysis on multiple statements.

func (*Analyzer) AnalyzeWithRegistryRules

func (a *Analyzer) AnalyzeWithRegistryRules(stmt any, dialect DialectInfo) []Diagnostic

AnalyzeWithRegistryRules runs all registered rules against the statement. This is an alias for Analyze for backward compatibility.

type CheckFunc

type CheckFunc func(stmt any, dialect DialectInfo, opts map[string]any) []Diagnostic

CheckFunc analyzes a statement and returns diagnostics. The stmt parameter is *core.SelectStmt passed as any to avoid import cycles. The opts parameter contains rule-specific options from configuration.

type Config

type Config struct {
	// DisabledRules contains rule IDs to skip
	DisabledRules map[string]bool

	// SeverityOverrides changes the default severity of rules
	SeverityOverrides map[string]core.Severity

	// RuleOptions contains rule-specific configuration
	RuleOptions map[string]map[string]any
}

Config controls which rules are enabled and their severity.

func NewConfig

func NewConfig() *Config

NewConfig creates a default configuration with all rules enabled.

func (*Config) Disable

func (c *Config) Disable(ruleID string) *Config

Disable disables a rule by ID.

func (*Config) GetRuleOptions

func (c *Config) GetRuleOptions(ruleID string) map[string]any

GetRuleOptions returns options for a specific rule.

func (*Config) GetSeverity

func (c *Config) GetSeverity(ruleID string, defaultSeverity core.Severity) core.Severity

GetSeverity returns the severity for a rule, applying any override.

func (*Config) IsDisabled

func (c *Config) IsDisabled(ruleID string) bool

IsDisabled returns true if the rule should be skipped.

func (*Config) SetRuleOptions

func (c *Config) SetRuleOptions(ruleID string, opts map[string]any) *Config

SetRuleOptions sets options for a specific rule.

func (*Config) SetSeverity

func (c *Config) SetSeverity(ruleID string, severity core.Severity) *Config

SetSeverity overrides the severity for a rule.

type Diagnostic

type Diagnostic struct {
	RuleID   string
	Severity core.Severity
	Message  string
	Pos      token.Position
	EndPos   token.Position // Optional: end of the problematic range
	Fixes    []Fix          // Optional: suggested fixes (for LSP code actions)

	// Remediation metadata
	DocumentationURL string        // URL to rule documentation, e.g., "https://leapsql.dev/docs/rules/AM01"
	ImpactScore      int           // 0-100, used for health score weighting
	AutoFixable      bool          // true if Fixes can be auto-applied
	RelatedInfo      []RelatedInfo // Additional locations/context
}

Diagnostic represents a lint finding.

type DialectInfo

type DialectInfo interface {
	GetName() string
	IsClauseToken(t token.TokenType) bool
	NormalizeName(name string) string
	IsWindow(name string) bool
}

DialectInfo is a minimal interface to avoid importing the full dialect package. Implemented by core.Dialect.

type Fix

type Fix struct {
	Description string
	TextEdits   []TextEdit
}

Fix represents a suggested code fix.

type ImpactLevel

type ImpactLevel int

ImpactLevel represents predefined impact score ranges.

const (
	// ImpactLow for minor issues (0-30)
	ImpactLow ImpactLevel = 20
	// ImpactMedium for moderate issues (31-60)
	ImpactMedium ImpactLevel = 50
	// ImpactHigh for significant issues (61-80)
	ImpactHigh ImpactLevel = 70
	// ImpactCritical for critical issues (81-100)
	ImpactCritical ImpactLevel = 90
)

func (ImpactLevel) Int

func (l ImpactLevel) Int() int

Int returns the impact score as an integer.

type ModelInfo

type ModelInfo struct {
	Path         string            // Model path (e.g., "staging.customers")
	Name         string            // Model name (e.g., "stg_customers")
	FilePath     string            // Absolute path to .sql file
	Type         core.ModelType    // Inferred or explicit model type
	Sources      []string          // Table references (deps)
	Columns      []core.ColumnInfo // Column-level lineage
	Materialized string            // table, view, incremental
	Tags         []string          // Metadata tags
	Meta         map[string]any    // Custom metadata
}

ModelInfo represents a model for project-level analysis. This mirrors the data needed from parser.ModelConfig without importing it.

type ProjectContext

type ProjectContext interface {
	// GetModels returns all models indexed by path.
	GetModels() map[string]ModelInfo

	// GetParents returns upstream model paths for a given model.
	GetParents(modelPath string) []string

	// GetChildren returns downstream model paths for a given model.
	GetChildren(modelPath string) []string

	// GetConfig returns the project health configuration.
	GetConfig() ProjectHealthConfig
}

ProjectContext provides access to project data for project-level rules. This is an interface to avoid import cycles between lint and project packages.

type ProjectHealthConfig

type ProjectHealthConfig struct {
	ModelFanoutThreshold        int // PM04: default 3
	TooManyJoinsThreshold       int // PM05: default 7
	PassthroughColumnThreshold  int // PL01: default 20
	StarlarkComplexityThreshold int // PT01: default 10
}

ProjectHealthConfig holds configurable thresholds for project health rules.

func DefaultProjectHealthConfig

func DefaultProjectHealthConfig() ProjectHealthConfig

DefaultProjectHealthConfig returns the default configuration.

type ProjectProvider

type ProjectProvider interface {
	Provider
	// AnalyzeProject runs project-level rules and returns diagnostics.
	// The context parameter provides all project data needed for analysis.
	AnalyzeProject(ctx ProjectContext) []Diagnostic
}

ProjectProvider analyzes project-level concerns. Implemented by the project health analyzer for DAG/architecture linting.

type ProjectRule

type ProjectRule interface {
	Rule

	// CheckProject analyzes the project context and returns diagnostics.
	CheckProject(ctx ProjectContext) []Diagnostic
}

ProjectRule analyzes project-level concerns. Implemented by rules that check DAG structure, naming conventions, etc.

func GetAllProjectRules

func GetAllProjectRules() []ProjectRule

GetAllProjectRules returns all registered project rules.

func GetProjectRuleByID

func GetProjectRuleByID(id string) (ProjectRule, bool)

GetProjectRuleByID returns a project rule by its ID.

func GetProjectRulesByGroup

func GetProjectRulesByGroup(group string) []ProjectRule

GetProjectRulesByGroup returns project rules in a specific group.

type Provider

type Provider interface {
	Name() string
}

Provider is the base interface for all lint providers.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry stores all registered lint rules (both SQL and project).

type RelatedInfo

type RelatedInfo struct {
	FilePath string
	Pos      token.Position
	Message  string
}

RelatedInfo provides additional context for a diagnostic.

type Rule

type Rule interface {
	// ID returns the unique identifier, e.g., "AM01" or "PM01"
	ID() string

	// Name returns the human-readable name, e.g., "ambiguous.distinct"
	Name() string

	// Group returns the category, e.g., "ambiguous", "structure", "modeling"
	Group() string

	// Description returns a human-readable description
	Description() string

	// DefaultSeverity returns the default severity for this rule
	DefaultSeverity() core.Severity

	// ConfigKeys returns configuration keys this rule accepts
	ConfigKeys() []string

	// Documentation methods for richer rule documentation
	Rationale() string   // Why this rule exists, what problems it prevents
	BadExample() string  // Code showing the anti-pattern
	GoodExample() string // Code showing the correct pattern
	Fix() string         // How to fix violations (when not obvious)
}

Rule is the base interface all lint rules implement. This provides a unified interface for both SQL-level and project-level rules.

func GetRuleByID

func GetRuleByID(id string) (Rule, bool)

GetRuleByID returns any rule by its ID, checking both SQL and project rules.

type RuleDef

type RuleDef struct {
	ID          string        // Unique identifier, e.g., "AM01" or "ansi/select-star"
	Name        string        // Human-readable name, e.g., "ambiguous.distinct"
	Group       string        // Category, e.g., "ambiguous", "structure", "convention"
	Description string        // Human-readable description
	Severity    core.Severity // Default severity
	Check       CheckFunc     // The check function
	ConfigKeys  []string      // Configuration keys this rule accepts (for rule-specific options)
	Dialects    []string      // Restrict to specific dialects; nil/empty means all dialects

	// Documentation fields for richer rule documentation
	Rationale   string // Why this rule exists, what problems it prevents
	BadExample  string // Code showing the anti-pattern
	GoodExample string // Code showing the correct pattern
	Fix         string // How to fix violations (when not obvious)
}

RuleDef is a data-driven rule definition used by dialects for dialect-specific rules. Rules are stateless - all context comes via the Check function parameters. The Check function receives an `any` type that should be *core.SelectStmt. This avoids import cycles between lint -> parser -> dialect -> lint.

type SQLProvider

type SQLProvider interface {
	Provider
	AnalyzeStatement(stmt any, dialect DialectInfo) []Diagnostic
}

SQLProvider analyzes individual SQL statements. Implemented by the SQL analyzer for statement-level linting.

type SQLRule

type SQLRule interface {
	Rule

	// CheckSQL analyzes a statement and returns diagnostics.
	// The stmt parameter is *core.SelectStmt passed as any to avoid import cycles.
	// The opts parameter contains rule-specific options from configuration.
	CheckSQL(stmt any, dialect DialectInfo, opts map[string]any) []Diagnostic

	// Dialects returns dialect restrictions; nil/empty means all dialects.
	Dialects() []string
}

SQLRule analyzes individual SQL statements. Implemented by rules that check SQL syntax and structure.

func GetAllSQLRules

func GetAllSQLRules() []SQLRule

GetAllSQLRules returns all registered SQL rules.

func GetSQLRuleByID

func GetSQLRuleByID(id string) (SQLRule, bool)

GetSQLRuleByID returns an SQL rule by its ID.

func GetSQLRulesByDialect

func GetSQLRulesByDialect(dialectName string) []SQLRule

GetSQLRulesByDialect returns SQL rules applicable to a specific dialect. Rules with empty/nil Dialects are included (they apply to all dialects).

func GetSQLRulesByGroup

func GetSQLRulesByGroup(group string) []SQLRule

GetSQLRulesByGroup returns SQL rules in a specific group.

func WrapRuleDef

func WrapRuleDef(def RuleDef) SQLRule

WrapRuleDef wraps a RuleDef to implement SQLRule interface.

type TextEdit

type TextEdit struct {
	Pos     token.Position
	EndPos  token.Position
	NewText string
}

TextEdit represents a text replacement.

Directories

Path Synopsis
Package project provides project-level linting for LeapSQL.
Package project provides project-level linting for LeapSQL.
rules
Package projectrules registers all project health lint rules.
Package projectrules registers all project health lint rules.
sql
Package sql provides SQL statement-level linting rules and analysis.
Package sql provides SQL statement-level linting rules and analysis.
internal/ast
Package ast provides AST traversal utilities for SQL lint rules.
Package ast provides AST traversal utilities for SQL lint rules.
rules
Package rules contains all SQL lint rules.
Package rules contains all SQL lint rules.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL