Fast, configurable static analyzer for Go projects.
Originally built to help AI agents understand codebases, but useful for any project.
- Rules in 8 categories — architecture, duplication, patterns, typesafety, security, deadcode, naming, documentation (
glint rulesprints the authoritative list) - Auto-fix support — automatic fixes for common issues (v1.1+)
- Single-pass analysis — files are read and parsed once, AST is cached
- Parallel execution — reading, parsing and rule evaluation use all CPU cores; findings stay byte-for-byte reproducible
- YAML configuration — with
extendsinheritance, severity overrides and per-rule exceptions - Multiple output formats — console, JSON, summary (optimized for AI agents)
- Go and TypeScript support — regex and AST-based analysis
go install github.com/aiseeq/glint/cmd/glint@latestOr build from source:
git clone https://github.com/aiseeq/glint.git
cd glint
make build# Analyze current directory
glint check
# Analyze specific paths
glint check ./backend ./frontend/shared
# Show only high+ severity issues
glint check --min-severity=high
# Run specific category
glint check --category=architecture
# Run specific rule
glint check --rule=error-masking
# Get summary for AI agents
glint check --output=summary
# Analyze a tree that does not compile as a whole (historical commits,
# git-ignored or generated sources, work in progress): packages that fail to
# type-check are reported and their files are analyzed without type information
glint check --tolerate-broken-packagestools/history/measure.py builds a quality curve over a repository's git
history: a slice every two weeks, each analyzed by today's full rule set
(project .glint.yaml exclusions are ignored, so the instrument stays the
same across all slices). Output is JSONL with per-slice aggregates:
findings per 1000 non-test Go lines, split by severity and category.
python3 tools/history/measure.py /path/to/repo curve.jsonl
python3 tools/history/plot.py curve.jsonl -o curve.png # needs matplotlibplot.py draws the heavy-findings curve (critical+high per 1000 lines) by
default; --metric per_kloc_total plots all findings, and passing several
JSONL files draws one line per project.
Create .glint.yaml in your project root:
version: 1
# Optional: start from another config file, resolved relative to this one.
extends: ../shared/glint-base.yaml
settings:
exclude:
- vendor/**
- node_modules/**
- "**/*_test.go"
min_severity: medium
output: console
categories:
architecture:
enabled: true
patterns:
severity_override: high # severity for every rule in this category
rules:
error-masking:
severity: critical # wins over the category override
exceptions:
- files: "**/config/**"
reason: "Config defaults are acceptable"
todo-comment:
enabled: false
typesafety:
enabled: trueReference:
| Key | Meaning |
|---|---|
extends |
Path to a base config merged under this one (relative to this file). |
settings.exclude |
Glob patterns; * stays inside one path segment, ** spans segments. A pattern without a separator also matches the base name. |
settings.skip_dirs |
Directory names never descended into. Defaults to .git .svn .hg .idea .vscode node_modules vendor .next out dist build bin — set it if one of those is a real package of yours. |
settings.min_severity |
low / medium / high / critical. |
settings.output |
console / json / summary. |
categories.<name>.enabled |
Defaults to true — naming a category to configure its rules does not switch it off. |
categories.<name>.severity_override |
Reported severity for every rule of the category. |
categories.<name>.rules.<rule>.severity |
Reported severity for one rule; wins over the category override. |
categories.<name>.rules.<rule>.exceptions |
file / files / line / pattern / function + reason. |
Individual findings can also be silenced at the source with //nolint:<rule> or
// <rule>: safe — reason, on the offending line or the line above it.
Rules are organized into 8 categories: architecture, deadcode, documentation, duplication, naming, patterns, security, typesafety. The authoritative, always-current list — names, severities and auto-fix availability — comes from the tool itself:
glint rules- masked-error-in-or-condition (HIGH) —
if err != nil || x == nil { return zero, nil }masks a real failure as a valid zero value - constructor-nil-return (HIGH) — New* constructor without an error result that can return nil
- constructor-swallows-nil-dep (HIGH) — constructor logs a nil dependency and builds the object anyway
- log-and-return-zero (MEDIUM) — Error/Warn log followed by a zero-value return in a function without an error result
- frontend-money-arithmetic (HIGH) — client-side arithmetic over money values (parseFloat sums, reduce aggregation)
- any-in-public-contract (MEDIUM) — bare any/interface{} in exported results and map[string]any fields
- tombstone-comment (LOW) — comments describing deleted code ("removed", "УДАЛЕНО") — git history already remembers
- migration-duplicate-version (CRITICAL) — two different migrations sharing one version number; also missing up/down pairs
- test-external-service (HIGH) — a test builds a live vendor client, or gates itself with "skip unless the API key is set" — a gate that is open in exactly the environment the test runs in, since the key comes from
.env. Declare the real opt-in helper inguard_functionsto allow deliberate live runs - layer-violation (CRITICAL) — Detects violations of Handler→Service→Repository architecture
- import-direction (HIGH) — Detects imports that violate layered architecture direction
- hardcoded-secret (CRITICAL) — Detects passwords, API keys, tokens in code
- sensitive-query-param (HIGH) — Detects credentials and action tokens exposed in URLs (CWE-598)
- sql-injection (CRITICAL) — Detects SQL injection via string concatenation
- error-masking (CRITICAL) — Detects patterns that mask errors instead of handling them properly
- cyclomatic-complexity — Functions with too many decision paths (default: >10)
- cross-file-duplicate — Detects duplicate code blocks across different files
- unused-param — Function parameters that are never used
- naming-convention — Detects stuttering, ALL_CAPS, underscores in exported names
- doc-missing — Detects exported types/functions without documentation
- error-string-compare — Detects error comparisons via strings instead of errors.Is/errors.As
- error-wrap — Detects errors returned without context (should use %w)
- go-modern — Suggests modern Go 1.21+ alternatives (slices.Sort, built-in min/max)
- unused-symbol — Detects unused private functions, types, constants
- doc-links — Detects broken/placeholder URLs in documentation
Two equivalent inline forms, placed on the violation line or the line directly above; markers work only inside comments and match the rule name exactly. Comma-separated nolint lists are supported:
db := NewRepo(nil) //nolint:nil-di
db := NewRepo(nil) //nolint:gosec,nil-di
// nil-di: safe — repo is wired later by the DI container
db := NewRepo(nil)Always add the reason after the marker. Policy rules may opt out of suppression entirely (implement rules.SuppressionExempt; silent-config-error does).
- go-modern: May suggest iterator patterns for external library methods (e.g.,
router.Walk) that cannot be changed. - doc-links: May flag
localhostorexample.comin code comments used as format examples.
# List all rules
glint rules
# Exit status is non-zero when HIGH or CRITICAL findings are present.
# Explain specific rule
glint explain error-maskingHuman-readable output with colors and context.
glint check --output=json > report.jsonMachine-readable format for CI/CD integration.
glint check --output=summaryCompact output optimized for AI agents:
GLINT ANALYSIS SUMMARY
======================
Critical: 37 | High: 176 | Medium: 1324 | Low: 1141
TOP ISSUES:
1. [HIGH] error-masking: 62 violations
2. [MEDIUM] ignored-error: 791 violations
3. [MEDIUM] long-function: 587 violations
Files analyzed: 666 | Duration: 1.26s
Glint can automatically fix certain issues:
# Preview fixes (dry-run by default)
glint fix
# Fix specific rule
glint fix --rule=interface-any
# Actually apply fixes
glint fix --dry-run=false
# Apply fixes even with uncommitted changes
glint fix --dry-run=false --forceRules with an auto-fix are marked (auto-fix) in glint rules output.
- Dry-run by default — always preview changes first
- Git warning — warns if you have uncommitted changes
- Atomic — all fixes in a file are applied together
# Show which files are being analyzed
glint check --verbose
# Debug output for rule selection
glint check --debug--timing reports per-phase and per-rule durations to stderr — total and the
slowest single file per rule:
glint check --timingIf glint hangs on your project, run it with --timing and press Ctrl+C: the
report names the rule and file it is stuck on (or the loading phase, if
type-checking is the problem). Please attach that output when filing an issue.
glint/
├── cmd/glint/ # CLI entry point
├── pkg/
│ ├── core/ # Walker, parser, config, cache
│ ├── fix/ # Auto-fix implementations
│ ├── rules/ # Rule implementations by category
│ └── output/ # Output formatters
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-rule) - Add tests for your changes
- Run tests (
go test ./...) - Commit your changes (
git commit -m 'Add amazing-rule') - Push to the branch (
git push origin feature/amazing-rule) - Open a Pull Request
MIT License. See LICENSE for details.