Documentation
¶
Index ¶
- type AnonInterfaceDegradationRule
- type AppendAssignRule
- type AuditActorPropagationRule
- func (r *AuditActorPropagationRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
- func (r *AuditActorPropagationRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
- func (r *AuditActorPropagationRule) Configure(settings map[string]any) error
- func (r *AuditActorPropagationRule) RequiresSSA() bool
- type BoolCompareRule
- type ConstructorNilReturnRule
- type ConstructorSwallowsNilDepRule
- type ContextBackgroundRule
- type ContextFirstRule
- type DeferInLoopRule
- type DeprecatedIoutilRule
- type DeprecatedNginxHTTP2ListenRule
- type DeterministicUUIDRule
- type E2EBlindWaitRule
- type EmptyBlockRule
- type EmptyStructReturnRule
- type ErrorLengthCheckRule
- type ErrorMaskedAsFalseBoolRule
- type ErrorMaskingRule
- type ErrorStringCompareRule
- type ErrorStringRule
- type ErrorWrapRule
- type FallbackReturnRule
- type FinancialConstantsRule
- type FinancialDecimalFloatRule
- type FinancialFPRoundingRule
- type FinancialJSONFloatRule
- type FinancialRoundedDeltaRule
- type FrontendEnvFallbackRule
- type FrontendMoneyArithmeticRule
- type FrontendSilentCatchRule
- type GoModernRule
- type HTTPBodyCloseRule
- type IdempotencyCheckThenCreateRule
- type IgnoredErrorRule
- type LegacyCommentMarkerRule
- type LegacyIdentifierRule
- type LogAndReturnZeroRule
- type MagicNumberRule
- type MainReturnAfterErrorRule
- type MapIterationOrderRule
- type MaskedErrorOrConditionRule
- type MigrationDuplicateVersionRule
- type MockIdentifierRule
- type MultiWriteNoTransactionRule
- func (r *MultiWriteNoTransactionRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
- func (r *MultiWriteNoTransactionRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
- func (r *MultiWriteNoTransactionRule) Configure(settings map[string]any) error
- func (r *MultiWriteNoTransactionRule) RequiresSSA() bool
- type MutexLockRule
- type NilDIRule
- type NilSliceRule
- type NonAtomicStatusHistoryRule
- type NonCanonicalLoggerRule
- type NullableObjectCallRule
- type OrphanedInterfaceRule
- type PaginationBoundaryTruncationRule
- type ProviderCommandBeforeIntentPersistRule
- type ProviderCommandRetryRule
- type QuadraticLoopRule
- type QueryInLoopRule
- type RangeValPointerRule
- type ReactRemountKeyRule
- type RedundantCompatibilityRule
- type ReimplementedStdlibRule
- type ResponseTypeInFunctionRule
- type ReturnNilErrorRule
- type SQLRowsCloseRule
- type ScatteredConstructionRule
- type SelectStarStructScanRule
- type SelectThenWriteRaceRule
- type ShadowVariableRule
- type SilentConfigErrorRule
- type SilentErrorHandlingRule
- type SilentlyOptionalDependencyRule
- type SleepWithoutContextRule
- type StringConcatRule
- type TautologicalAssertionRule
- type TechDebtRule
- type TerminalAfterFailedCheckpointRule
- type TestExternalServiceRule
- func (r *TestExternalServiceRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
- func (r *TestExternalServiceRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
- func (r *TestExternalServiceRule) Configure(settings map[string]any) error
- func (r *TestExternalServiceRule) RequiresSSA() bool
- type TestSchemaMutationWithoutCleanupRule
- type TestWithoutAssertionRule
- type TimeEqualRule
- type TodoCommentRule
- type TombstoneCommentRule
- type TypeInferrer
- type TypeInfo
- type TypedNilIntoInterfaceRule
- type UnboundedResponseReadRule
- type UnboundedSyncMapRule
- type UnfalsifiableTestCaseRule
- type UnguardedSharedFieldRule
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AnonInterfaceDegradationRule ¶ added in v1.4.2
AnonInterfaceDegradationRule detects type assertions on anonymous interfaces followed by silent degradation returns. This pattern often indicates dead delegation code.
Catches patterns like:
if x.(interface{ Method() Type }); ok {
return x.Method()
}
return zeroValue // Problem: silently degrades
This violates "Fail explicitly, never degrade silently"
func NewAnonInterfaceDegradationRule ¶ added in v1.4.2
func NewAnonInterfaceDegradationRule() *AnonInterfaceDegradationRule
NewAnonInterfaceDegradationRule creates the rule
func (*AnonInterfaceDegradationRule) AnalyzeFile ¶ added in v1.4.2
func (r *AnonInterfaceDegradationRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for anonymous interface degradation patterns
type AppendAssignRule ¶
AppendAssignRule detects append() calls without assignment
func NewAppendAssignRule ¶
func NewAppendAssignRule() *AppendAssignRule
NewAppendAssignRule creates the rule
func (*AppendAssignRule) AnalyzeFile ¶
func (r *AppendAssignRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for append without assignment
type AuditActorPropagationRule ¶ added in v1.4.2
AuditActorPropagationRule detects loss of human actor and audit source attribution before typed audit sinks.
func NewAuditActorPropagationRule ¶ added in v1.4.2
func NewAuditActorPropagationRule() *AuditActorPropagationRule
NewAuditActorPropagationRule creates the package-level SSA rule.
func (*AuditActorPropagationRule) AnalyzeFile ¶ added in v1.4.2
func (r *AuditActorPropagationRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
AnalyzeFile is a no-op because this rule requires shared package SSA.
func (*AuditActorPropagationRule) AnalyzeGoProject ¶ added in v1.4.2
func (r *AuditActorPropagationRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
AnalyzeGoProject follows actor and source taint through context-sensitive SSA calls.
func (*AuditActorPropagationRule) Configure ¶ added in v1.4.2
func (r *AuditActorPropagationRule) Configure(settings map[string]any) error
Configure replaces the default sink names when sinks is explicitly set. Callers can supplement the defaults by including them in the configured list.
func (*AuditActorPropagationRule) RequiresSSA ¶ added in v1.4.2
func (r *AuditActorPropagationRule) RequiresSSA() bool
RequiresSSA reports that AnalyzeGoProject requires built SSA and its program.
type BoolCompareRule ¶
BoolCompareRule detects redundant boolean comparisons
func NewBoolCompareRule ¶
func NewBoolCompareRule() *BoolCompareRule
NewBoolCompareRule creates the rule
func (*BoolCompareRule) AnalyzeFile ¶
func (r *BoolCompareRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for redundant boolean comparisons
type ConstructorNilReturnRule ¶ added in v1.4.2
ConstructorNilReturnRule detects constructors (New*) without an error result that can return nil:
func NewWalletRepository(userRepo interface{}) WalletRepository {
repo, ok := userRepo.(*UserRepository)
if !ok {
return nil // callers silently receive a nil dependency
}
...
}
Callers rarely nil-check constructor results, so the failure surfaces much later as a panic far from its cause. CLAUDE.md: initialization failures must be explicit — change the signature to (T, error).
Scope is kept narrow for precision: plain functions named New* with a single non-error result. Comma-ok contracts (T, bool), methods on factories, and closures are out of scope.
func NewConstructorNilReturnRule ¶ added in v1.4.2
func NewConstructorNilReturnRule() *ConstructorNilReturnRule
NewConstructorNilReturnRule creates the rule
func (*ConstructorNilReturnRule) AnalyzeFile ¶ added in v1.4.2
func (r *ConstructorNilReturnRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks Go constructors for silent nil returns
type ConstructorSwallowsNilDepRule ¶ added in v1.4.2
ConstructorSwallowsNilDepRule detects constructors that notice a nil dependency, log it — and build the object anyway:
func NewPermissionManager(repo Repository) *PermissionManager {
if repo == nil {
logger.Error("Critical: repo is nil") // and continues!
}
return &PermissionManager{repo: repo}
}
This is the "graceful degradation" anti-pattern forbidden by CLAUDE.md: the caller receives a half-alive object and the failure surfaces far from its cause. A nil dependency must abort construction with an error.
Not flagged: returning an error, panicking, or assigning a default to the parameter (options-defaulting), and Debug/Info-level notes.
func NewConstructorSwallowsNilDepRule ¶ added in v1.4.2
func NewConstructorSwallowsNilDepRule() *ConstructorSwallowsNilDepRule
NewConstructorSwallowsNilDepRule creates the rule
func (*ConstructorSwallowsNilDepRule) AnalyzeFile ¶ added in v1.4.2
func (r *ConstructorSwallowsNilDepRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks Go constructors for swallowed nil dependencies
type ContextBackgroundRule ¶
ContextBackgroundRule detects context.Background/TODO usage in functions that receive context
func NewContextBackgroundRule ¶
func NewContextBackgroundRule() *ContextBackgroundRule
NewContextBackgroundRule creates the rule
func (*ContextBackgroundRule) AnalyzeFile ¶
func (r *ContextBackgroundRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for context.Background/TODO misuse
type ContextFirstRule ¶ added in v1.4.2
ContextFirstRule detects a function that needs a context, does not accept one, and therefore invents its own:
func (s *Service) SyncWallet(address string) error {
ctx := context.Background() // the caller's deadline is gone
return s.repo.Save(ctx, address)
}
The call chain silently stops being cancellable at this function: a request the user aborted keeps running, a shutdown waits for work nobody needs, and a deadline set three frames up applies to nothing below.
The rule looks at what the function does, not at what it is called: a function that hands no context to anything needs none, whatever its name. Functions that already accept a context are the business of context-background, which reports the same misuse from the other side.
Not flagged: package main and the entry points init/TestMain, where the root context of the program has to come from somewhere; contexts created for a goroutine that outlives the call; and contexts whose cancel function is stored rather than deferred, which belong to something started here and stopped elsewhere.
func NewContextFirstRule ¶ added in v1.4.2
func NewContextFirstRule() *ContextFirstRule
NewContextFirstRule creates the rule
func (*ContextFirstRule) AnalyzeFile ¶ added in v1.4.2
func (r *ContextFirstRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile reports every function that manufactures the context it should have been given.
type DeferInLoopRule ¶
DeferInLoopRule detects defer statements inside loops
func NewDeferInLoopRule ¶
func NewDeferInLoopRule() *DeferInLoopRule
NewDeferInLoopRule creates the rule
func (*DeferInLoopRule) AnalyzeFile ¶
func (r *DeferInLoopRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for defer inside loops
type DeprecatedIoutilRule ¶
DeprecatedIoutilRule detects usage of deprecated io/ioutil package
func NewDeprecatedIoutilRule ¶
func NewDeprecatedIoutilRule() *DeprecatedIoutilRule
NewDeprecatedIoutilRule creates the rule
func (*DeprecatedIoutilRule) AnalyzeFile ¶
func (r *DeprecatedIoutilRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for io/ioutil usage
type DeprecatedNginxHTTP2ListenRule ¶ added in v1.4.2
DeprecatedNginxHTTP2ListenRule detects syntax deprecated by Nginx 1.25.1.
func NewDeprecatedNginxHTTP2ListenRule ¶ added in v1.4.2
func NewDeprecatedNginxHTTP2ListenRule() *DeprecatedNginxHTTP2ListenRule
NewDeprecatedNginxHTTP2ListenRule creates the rule.
func (*DeprecatedNginxHTTP2ListenRule) AnalyzeFile ¶ added in v1.4.2
func (r *DeprecatedNginxHTTP2ListenRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks complete Nginx directives and ignores comments.
type DeterministicUUIDRule ¶ added in v1.4.2
DeterministicUUIDRule detects patterns where UUIDs are generated from strings (email, namespace) instead of using real UUIDs from the database. Principle: "ID always comes from DB, never computed"
func NewDeterministicUUIDRule ¶ added in v1.4.2
func NewDeterministicUUIDRule() *DeterministicUUIDRule
NewDeterministicUUIDRule creates the rule
func (*DeterministicUUIDRule) AnalyzeFile ¶ added in v1.4.2
func (r *DeterministicUUIDRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for deterministic UUID generation patterns
type E2EBlindWaitRule ¶ added in v1.4.2
E2EBlindWaitRule detects browser-test waits that are not tied to the fact under test: `waitForLoadState('networkidle')`, `goto(..., { waitUntil: 'networkidle' })` and `waitForTimeout(N)`.
Такое ожидание врёт в обе стороны. На странице с фоновыми запросами тишина в сети не наступает вовсе, и тест падает по таймауту, ничего не проверив; на странице, которая рисует данные вторым кадром, тишина наступает раньше отрисовки, и тест читает пустую разметку. Слепая пауза `waitForTimeout` добавляет к этому зависимость от скорости машины.
Проверяемый факт всегда конкретен: появился локатор, сменился URL, пришло значение. На него и надо ждать — тогда ожидание и есть проверка.
Третий случай того же семейства — проверка нового URL сразу после действия, которое этот переход запускает. Клиентский роутер уводит со страницы асинхронно, поэтому `expect(page.url()).toContain('/onboarding')` читает ещё старый адрес; ждать надо сам переход через waitForURL.
networkidle внутри try/catch не флагуется: там это мягкая синхронизация с явным запасным путём, а не единственное условие готовности.
func NewE2EBlindWaitRule ¶ added in v1.4.2
func NewE2EBlindWaitRule() *E2EBlindWaitRule
NewE2EBlindWaitRule creates the rule.
func (*E2EBlindWaitRule) AnalyzeFile ¶ added in v1.4.2
func (r *E2EBlindWaitRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile scans browser tests and their helpers line by line.
type EmptyBlockRule ¶
EmptyBlockRule detects empty if/for/switch blocks
func NewEmptyBlockRule ¶
func NewEmptyBlockRule() *EmptyBlockRule
NewEmptyBlockRule creates the rule
func (*EmptyBlockRule) AnalyzeFile ¶
func (r *EmptyBlockRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for empty blocks
type EmptyStructReturnRule ¶ added in v1.4.2
EmptyStructReturnRule detects functions that return empty structs with nil error instead of returning explicit error. This violates "Fail explicitly, never degrade silently" Catches: return SafeDecimal{}, nil (in error context) Catches: return Config{} (without error, in error context)
func NewEmptyStructReturnRule ¶ added in v1.4.2
func NewEmptyStructReturnRule() *EmptyStructReturnRule
NewEmptyStructReturnRule creates the rule
func (*EmptyStructReturnRule) AnalyzeFile ¶ added in v1.4.2
func (r *EmptyStructReturnRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for empty struct returns in error contexts
type ErrorLengthCheckRule ¶ added in v1.4.2
ErrorLengthCheckRule detects dangerous patterns where error type is determined by string length This is an anti-pattern because: - Any error message of certain length will be misclassified - Error message length depends on driver version, locale, etc. - Real errors get masked as different error types
func NewErrorLengthCheckRule ¶ added in v1.4.2
func NewErrorLengthCheckRule() *ErrorLengthCheckRule
NewErrorLengthCheckRule creates the rule
func (*ErrorLengthCheckRule) AnalyzeFile ¶ added in v1.4.2
func (r *ErrorLengthCheckRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for error length check patterns
type ErrorMaskedAsFalseBoolRule ¶ added in v1.4.2
ErrorMaskedAsFalseBoolRule detects `if err != nil { return false }` patterns inside non-predicate bool-returning functions.
The existing `error-masking` rule requires the function to return `error`. That misses a class of security-sensitive bugs where a `(...) bool` function internally calls an error-returning API, then conflates "error" with "not allowed":
func ValidateUserPermission(user, perm string) bool {
permissions, err := c.GetRolePermissions(user)
if err != nil {
return false // ← user gets denied for reasons they can't debug;
// ops can't see that lookup is broken
}
...
}
Pure predicates (IsEnabled, HasRole, CanWrite, ShouldRetry) are exempt — returning false on lookup miss is their whole contract.
Detects:
- `if err != nil { ... return false ... }` without any logging call
- Function's return type contains `bool` (any position, not just last)
- Function name does NOT start with Is/Has/Can/Should
Skips:
- Test files
- Pure predicate functions (Is/Has/Can/Should prefix)
- Blocks that log the error before returning false
func NewErrorMaskedAsFalseBoolRule ¶ added in v1.4.2
func NewErrorMaskedAsFalseBoolRule() *ErrorMaskedAsFalseBoolRule
NewErrorMaskedAsFalseBoolRule creates the rule
func (*ErrorMaskedAsFalseBoolRule) AnalyzeFile ¶ added in v1.4.2
func (r *ErrorMaskedAsFalseBoolRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile runs the rule.
type ErrorMaskingRule ¶
ErrorMaskingRule detects patterns that mask errors instead of handling them properly This implements CLAUDE.md principle: "Fail explicitly, never degrade silently"
func NewErrorMaskingRule ¶
func NewErrorMaskingRule() *ErrorMaskingRule
NewErrorMaskingRule creates the rule
func (*ErrorMaskingRule) AnalyzeFile ¶
func (r *ErrorMaskingRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for error masking patterns
type ErrorStringCompareRule ¶ added in v1.3.0
ErrorStringCompareRule detects error comparisons using string matching
func NewErrorStringCompareRule ¶ added in v1.3.0
func NewErrorStringCompareRule() *ErrorStringCompareRule
NewErrorStringCompareRule creates the rule
func (*ErrorStringCompareRule) AnalyzeFile ¶ added in v1.3.0
func (r *ErrorStringCompareRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for error string comparisons
type ErrorStringRule ¶
ErrorStringRule detects error strings that don't follow Go conventions
func NewErrorStringRule ¶
func NewErrorStringRule() *ErrorStringRule
NewErrorStringRule creates the rule
func (*ErrorStringRule) AnalyzeFile ¶
func (r *ErrorStringRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks error string formatting
type ErrorWrapRule ¶ added in v1.3.0
ErrorWrapRule detects errors returned without context wrapping
func NewErrorWrapRule ¶ added in v1.3.0
func NewErrorWrapRule() *ErrorWrapRule
NewErrorWrapRule creates the rule
func (*ErrorWrapRule) AnalyzeFile ¶ added in v1.3.0
func (r *ErrorWrapRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for unwrapped error returns
type FallbackReturnRule ¶ added in v1.4.2
FallbackReturnRule detects fallback patterns that silently degrade instead of failing explicitly This implements CLAUDE.md principle: "Fail explicitly, never degrade silently" Catches: return testProvider, return mockService on errors Excludes: functions with "OrDefault" in name, parse* functions, singleton getters, middleware defensive code
func NewFallbackReturnRule ¶ added in v1.4.2
func NewFallbackReturnRule() *FallbackReturnRule
NewFallbackReturnRule creates the rule
func (*FallbackReturnRule) AnalyzeFile ¶ added in v1.4.2
func (r *FallbackReturnRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for fallback return patterns
type FinancialConstantsRule ¶ added in v1.4.2
FinancialConstantsRule detects hardcoded financial constants that should be in config Examples: fees, commissions, rates, percentages in financial context
func NewFinancialConstantsRule ¶ added in v1.4.2
func NewFinancialConstantsRule() *FinancialConstantsRule
NewFinancialConstantsRule creates the rule
func (*FinancialConstantsRule) AnalyzeFile ¶ added in v1.4.2
func (r *FinancialConstantsRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for hardcoded financial constants
type FinancialDecimalFloatRule ¶ added in v1.4.2
FinancialDecimalFloatRule detects discarded exactness from financial decimal conversions.
func NewFinancialDecimalFloatRule ¶ added in v1.4.2
func NewFinancialDecimalFloatRule() *FinancialDecimalFloatRule
NewFinancialDecimalFloatRule creates the rule.
func (*FinancialDecimalFloatRule) AnalyzeFile ¶ added in v1.4.2
func (r *FinancialDecimalFloatRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks two-result Float64 assignments on shopspring decimal values.
type FinancialFPRoundingRule ¶ added in v1.4.2
FinancialFPRoundingRule detects unsafe floor/ceil/trunc rounding of money values multiplied by 100 (or by an explicit percentage). Pattern `Math.floor(value * 100) / 100` looks like "round down to cent" but JavaScript IEEE-754 makes `5055.19 * 100 = 505518.99999999994`, which floors to `505518` and yields `5055.18` — silently losing one cent.
The same shimmer hits `Math.floor(money * pct) / 100` for percentage buttons (e.g. 100% of max → 5055.18 instead of 5055.19).
Safe alternatives:
- `Math.round(value * 100) / 100` — half-even, no shimmer for cent grid
- `Math.floor(value * 100 + 1e-9) / 100` — explicit epsilon
- `value.toFixed(2)` when half-up rounding is acceptable
- In Go: use `decimal.Decimal` arithmetic, never float64
func NewFinancialFPRoundingRule ¶ added in v1.4.2
func NewFinancialFPRoundingRule() *FinancialFPRoundingRule
NewFinancialFPRoundingRule creates the rule
func (*FinancialFPRoundingRule) AnalyzeFile ¶ added in v1.4.2
func (r *FinancialFPRoundingRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for floating-point rounding of money
type FinancialJSONFloatRule ¶ added in v1.4.2
FinancialJSONFloatRule detects precision-losing floats in monetary JSON contracts.
func NewFinancialJSONFloatRule ¶ added in v1.4.2
func NewFinancialJSONFloatRule() *FinancialJSONFloatRule
NewFinancialJSONFloatRule creates the rule.
func (*FinancialJSONFloatRule) AnalyzeFile ¶ added in v1.4.2
func (r *FinancialJSONFloatRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks JSON DTO structs while preserving financial context through anonymous fields.
type FinancialRoundedDeltaRule ¶ added in v1.4.2
FinancialRoundedDeltaRule detects financial deltas derived by subtracting parsed cumulative money fields. Financial deltas should be calculated in the canonical backend/domain layer from full-precision decimals and exposed as a first-class field, not reconstructed from rounded API/display values.
func NewFinancialRoundedDeltaRule ¶ added in v1.4.2
func NewFinancialRoundedDeltaRule() *FinancialRoundedDeltaRule
NewFinancialRoundedDeltaRule creates the rule
func (*FinancialRoundedDeltaRule) AnalyzeFile ¶ added in v1.4.2
func (r *FinancialRoundedDeltaRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for deltas computed from parsed cumulative money fields
type FrontendEnvFallbackRule ¶ added in v1.4.2
FrontendEnvFallbackRule detects frontend public-env patterns that silently degrade at build/runtime instead of failing explicitly.
func NewFrontendEnvFallbackRule ¶ added in v1.4.2
func NewFrontendEnvFallbackRule() *FrontendEnvFallbackRule
NewFrontendEnvFallbackRule creates the rule
func (*FrontendEnvFallbackRule) AnalyzeFile ¶ added in v1.4.2
func (r *FrontendEnvFallbackRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for placeholder and fallback public environment configuration
type FrontendMoneyArithmeticRule ¶ added in v1.4.2
type FrontendMoneyArithmeticRule struct {
*rules.BaseRule
// contains filtered or unexported fields
}
FrontendMoneyArithmeticRule detects client-side arithmetic over money values in TS/JS:
investedAmount += parseFloat(inv.amount) pending.reduce((sum, w) => sum + parseFloat(w.amount || '0'), 0)
Financial aggregates must be computed on the backend (single canonical calculation logic); parseFloat over decimal strings silently loses precision, and duplicated client math diverges from the server.
Not flagged: pure formatting (formatAmount(parseFloat(x))), comparisons, non-money numerics, tests.
func NewFrontendMoneyArithmeticRule ¶ added in v1.4.2
func NewFrontendMoneyArithmeticRule() *FrontendMoneyArithmeticRule
NewFrontendMoneyArithmeticRule creates the rule
func (*FrontendMoneyArithmeticRule) AnalyzeFile ¶ added in v1.4.2
func (r *FrontendMoneyArithmeticRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks TS/JS lines for money arithmetic
type FrontendSilentCatchRule ¶ added in v1.4.2
FrontendSilentCatchRule detects frontend catch blocks that only log errors. UI code must either surface the failure to the user or rethrow it to a caller that can do so.
func NewFrontendSilentCatchRule ¶ added in v1.4.2
func NewFrontendSilentCatchRule() *FrontendSilentCatchRule
NewFrontendSilentCatchRule creates the rule
func (*FrontendSilentCatchRule) AnalyzeFile ¶ added in v1.4.2
func (r *FrontendSilentCatchRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for catch blocks that log without user-visible handling
type GoModernRule ¶ added in v1.4.0
GoModernRule detects patterns that could use modern Go features
func NewGoModernRule ¶ added in v1.4.0
func NewGoModernRule() *GoModernRule
NewGoModernRule creates the rule
func (*GoModernRule) AnalyzeFile ¶ added in v1.4.0
func (r *GoModernRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for outdated patterns
type HTTPBodyCloseRule ¶
HTTPBodyCloseRule detects HTTP response body not being closed
func NewHTTPBodyCloseRule ¶
func NewHTTPBodyCloseRule() *HTTPBodyCloseRule
NewHTTPBodyCloseRule creates the rule
func (*HTTPBodyCloseRule) AnalyzeFile ¶
func (r *HTTPBodyCloseRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for unclosed HTTP response bodies
type IdempotencyCheckThenCreateRule ¶ added in v1.4.2
IdempotencyCheckThenCreateRule detects non-atomic idempotency checks followed by a create on the same repository.
func NewIdempotencyCheckThenCreateRule ¶ added in v1.4.2
func NewIdempotencyCheckThenCreateRule() *IdempotencyCheckThenCreateRule
NewIdempotencyCheckThenCreateRule creates the rule.
func (*IdempotencyCheckThenCreateRule) AnalyzeFile ¶ added in v1.4.2
func (r *IdempotencyCheckThenCreateRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks each production function independently.
type IgnoredErrorRule ¶
IgnoredErrorRule detects error values thrown away with the blank identifier.
Whether a returned value is an error is decided by its type, not by the name of the function returning it. The name-based version of this rule matched a fixed list of verbs (Read, Parse, Query, Marshal…) and therefore stayed silent on every domain method — the shape `items, _ = repo.List(ctx)` reads as "no data" downstream while the query may well have failed (REF-462).
Closing a resource stays exempt: `_ = conn.Close()` and the print family cannot report anything useful to the caller, and writing the blank there is the documented way to say "checked and deliberately dropped".
func NewIgnoredErrorRule ¶
func NewIgnoredErrorRule() *IgnoredErrorRule
NewIgnoredErrorRule creates a new ignored error detector
func (*IgnoredErrorRule) AnalyzeFile ¶
func (r *IgnoredErrorRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
AnalyzeFile is unused: the rule works on the typed project.
func (*IgnoredErrorRule) AnalyzeGoProject ¶ added in v1.4.2
func (r *IgnoredErrorRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
AnalyzeGoProject walks assignments and reports blanks that swallow an error value.
func (*IgnoredErrorRule) RequiresSSA ¶ added in v1.4.2
func (r *IgnoredErrorRule) RequiresSSA() bool
RequiresSSA reports that plain type information is enough.
type LegacyCommentMarkerRule ¶ added in v1.4.2
LegacyCommentMarkerRule detects inline comments that document a runtime legacy code path — "Legacy mode", "Legacy compatibility", "legacy SSE auth", "(legacy)" — as distinct from identifier names (covered by legacy-identifier) and godoc-level Deprecated comments (covered by deprecated-comment).
Why inline: composite_router.go:831 has `// 2. Legacy mode: separate admin/ user path handling` — not a symbol name, not a godoc. The comment itself admits a runtime legacy branch exists, which CLAUDE.md's "No legacy, only current code" forbids.
Detects in Go and TypeScript/TSX files:
- `// Legacy mode`, `// Legacy compatibility`, `// Legacy:`
- `// legacy foo`, `// (legacy)`, `// SMTP_* (legacy)`
- multiline /* Legacy ... */ / /** Legacy ... */ prefix
Skips:
- Test files and generated code
- Comments that quote CLAUDE.md policy (contain "CLAUDE.md", "No legacy", "policy", "запрет", "запрещ") — self-references to the rule itself
- //nolint:legacy-comment-marker on the line
- The legacy-identifier rule's own file (which prints "Legacy" as a string literal for diagnostic messages — it's the rule implementation, not a legacy code path)
func NewLegacyCommentMarkerRule ¶ added in v1.4.2
func NewLegacyCommentMarkerRule() *LegacyCommentMarkerRule
NewLegacyCommentMarkerRule creates the rule
func (*LegacyCommentMarkerRule) AnalyzeFile ¶ added in v1.4.2
func (r *LegacyCommentMarkerRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile scans line-by-line for legacy comments in Go and TS files.
type LegacyIdentifierRule ¶ added in v1.4.2
LegacyIdentifierRule detects identifiers (func/method/type/const/var) whose name contains "Legacy" / "legacy_". Comments are covered by the separate `deprecated-comment` rule; this one exists because renaming a symbol to include "Legacy" is a common way to ship a parallel implementation that never actually gets removed — the mirror of what CLAUDE.md forbids under "No legacy, only current code".
Detects:
- func (Foo) RegisterLegacyRoutes(...) — method with Legacy in name
- func buildLegacyPayload(...) — function with Legacy in name
- type LegacyUser struct{} — type with Legacy prefix/suffix
- var/const LegacyTimeout = ... — value identifier
Skips:
- Test files
- Generated files (*.gen.go, *_gen.go, /generated/)
- //nolint:legacy-identifier opt-outs on the declaration line
func NewLegacyIdentifierRule ¶ added in v1.4.2
func NewLegacyIdentifierRule() *LegacyIdentifierRule
NewLegacyIdentifierRule creates the rule
func (*LegacyIdentifierRule) AnalyzeFile ¶ added in v1.4.2
func (r *LegacyIdentifierRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for Legacy identifiers
type LogAndReturnZeroRule ¶ added in v1.4.2
LogAndReturnZeroRule detects functions without an error result that log at Error/Warn level and immediately return a zero value:
func (m *TokenManager) getJWTIssuer() string {
if m.config == nil {
m.logger.Error("Configuration not available")
return "" // empty issuer breaks validation much later
}
...
}
The log acknowledges a failure, but the caller receives a sentinel ("" / 0 / nil) indistinguishable from a valid value. CLAUDE.md: a function that can fail must return (T, error).
Not flagged: Info/Debug logs, `return false` (see error-masked-as-false-bool), computed recovery values, and HTTP handlers (they report the failure via ResponseWriter).
func NewLogAndReturnZeroRule ¶ added in v1.4.2
func NewLogAndReturnZeroRule() *LogAndReturnZeroRule
NewLogAndReturnZeroRule creates the rule
func (*LogAndReturnZeroRule) AnalyzeFile ¶ added in v1.4.2
func (r *LogAndReturnZeroRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks Go functions for the log-then-zero pattern
type MagicNumberRule ¶
MagicNumberRule detects hardcoded numbers that should be named constants
func NewMagicNumberRule ¶
func NewMagicNumberRule() *MagicNumberRule
NewMagicNumberRule creates the rule
func (*MagicNumberRule) AnalyzeFile ¶
func (r *MagicNumberRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for magic numbers
type MainReturnAfterErrorRule ¶ added in v1.4.2
MainReturnAfterErrorRule detects func main returning normally from an error branch:
func main() {
if err := run(); err != nil {
log.Printf("Error: %v", err)
return // the process exits 0 — scripts and CI see success
}
}
A bare return in main ends the process with exit code 0, so every caller — shell scripts, CI, cron — treats the failed run as successful. The honest endings are os.Exit(1), log.Fatal or panic; those forms are not flagged.
Родилось из ревью projectD 2026-08 (№27): шесть cmd-утилит логировали ошибку и выходили из main обычным return, отчитываясь кодом 0.
func NewMainReturnAfterErrorRule ¶ added in v1.4.2
func NewMainReturnAfterErrorRule() *MainReturnAfterErrorRule
NewMainReturnAfterErrorRule creates the rule
func (*MainReturnAfterErrorRule) AnalyzeFile ¶ added in v1.4.2
func (r *MainReturnAfterErrorRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks func main of package main for returns from error branches
type MapIterationOrderRule ¶ added in v1.4.2
MapIterationOrderRule detects values whose order comes from walking a map and then leaves the function:
for area := range detected {
areas = append(areas, area) // order is random
}
return areas
Go randomizes map iteration deliberately, so the same input produces a different order on every run. Once such a slice or string reaches a message, a report or a caller, the output stops being reproducible: golden tests flap, CI diffs show phantom changes, and findings swap places between runs.
Not flagged: order-independent aggregation (sums, counters), collecting into another map, values that never leave the function, and slices sorted before they are used.
func NewMapIterationOrderRule ¶ added in v1.4.2
func NewMapIterationOrderRule() *MapIterationOrderRule
NewMapIterationOrderRule creates the rule
func (*MapIterationOrderRule) AnalyzeFile ¶ added in v1.4.2
func (r *MapIterationOrderRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
AnalyzeFile is a no-op because this rule needs the package's type information to tell a map range from a slice range.
func (*MapIterationOrderRule) AnalyzeGoProject ¶ added in v1.4.2
func (r *MapIterationOrderRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
AnalyzeGoProject inspects every function of the loaded packages.
func (*MapIterationOrderRule) RequiresSSA ¶ added in v1.4.2
func (r *MapIterationOrderRule) RequiresSSA() bool
RequiresSSA reports that typed syntax is enough for this rule.
type MaskedErrorOrConditionRule ¶ added in v1.4.2
MaskedErrorOrConditionRule detects branches that conflate a real error with a legitimate "no data" case via ||, then swallow the error:
if err != nil || latest == nil {
return SafeDecimal{}, nil // DB failure masked as valid zero value
}
The caller cannot distinguish a storage failure from an honest zero. CLAUDE.md: "Fail explicitly, never degrade silently".
Functions WITHOUT an error result are covered too, and there any return from such a branch masks the failure — the error cannot even be handed back:
func (s *S) dayYield(id string) Decimal {
share, err := s.share(id)
if err != nil || share.LessThanOrEqual(zero) {
return s.cumulative // DB failure silently yields "no growth"
}
Not flagged: branches that propagate/wrap the error, branches that handle the error in a nested if, branches that panic, &&-narrowing (errors.Is style), and branches without a return.
For functions WITHOUT an error result the log IS the only error channel, so a branch that mentions the error variable anywhere (logs it, hands it to a collector) or calls an Error/Warn/Fatal-level logger is treated as handled. Functions taking http.ResponseWriter are skipped entirely: an HTTP handler reports failures through the response, not through return values. Functions WITH an error result get no such exemption — logging and then returning nil error still hides the failure from the caller.
func NewMaskedErrorOrConditionRule ¶ added in v1.4.2
func NewMaskedErrorOrConditionRule() *MaskedErrorOrConditionRule
NewMaskedErrorOrConditionRule creates the rule
func (*MaskedErrorOrConditionRule) AnalyzeFile ¶ added in v1.4.2
func (r *MaskedErrorOrConditionRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks Go functions for the masking pattern
type MigrationDuplicateVersionRule ¶ added in v1.4.2
type MigrationDuplicateVersionRule struct {
*rules.BaseRule
// contains filtered or unexported fields
}
MigrationDuplicateVersionRule detects two different migrations sharing one version number in the same directory:
000029_client_number.up.sql 000029_support_conversations.up.sql // same version, different migration!
Version-keyed migrators (maps, golang-migrate) silently keep only one of them — a fresh database ends up missing the other migration's schema. Every migration must own a unique version number, and the migrator itself should fail on duplicates (defense-in-depth: this rule catches the problem at lint time).
func NewMigrationDuplicateVersionRule ¶ added in v1.4.2
func NewMigrationDuplicateVersionRule() *MigrationDuplicateVersionRule
NewMigrationDuplicateVersionRule creates the rule
func (*MigrationDuplicateVersionRule) AnalyzeFile ¶ added in v1.4.2
func (r *MigrationDuplicateVersionRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile registers migration files and reports duplicate versions
func (*MigrationDuplicateVersionRule) ResetState ¶ added in v1.4.2
func (r *MigrationDuplicateVersionRule) ResetState()
ResetState clears the migrations seen so far, so that a project root never inherits versions registered while analyzing a previous root.
type MockIdentifierRule ¶ added in v1.4.2
MockIdentifierRule detects identifiers (func/method/type/const/var) whose name contains a Mock/Fake/Stub/Dummy segment in non-test code. Such a name in production either mislabels a real code path (a "mock" quote that is in fact the live path for one execution mode — readers skip it as test-only and it silently drifts from the implementation it mirrors) or marks test scaffolding that leaked out of _test.go files. Deliberate simulation surfaces (a config-gated sandbox mode) stay, renamed to say what they do or opted out with //nolint:mock-identifier and a reason.
Detects:
- func (a *Admin) createMockQuote(...) — method with Mock in name
- func buildFakePayload(...) — function with Fake in name
- type StubNotifier struct{} — type with Stub prefix
- var dummy_response = ... — snake_case value identifier
Skips:
- Test files
- Generated files (*.gen.go, *_gen.go, /generated/)
- //nolint:mock-identifier opt-outs on the declaration line
func NewMockIdentifierRule ¶ added in v1.4.2
func NewMockIdentifierRule() *MockIdentifierRule
NewMockIdentifierRule creates the rule
func (*MockIdentifierRule) AnalyzeFile ¶ added in v1.4.2
func (r *MockIdentifierRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for Mock/Fake/Stub/Dummy identifiers
type MultiWriteNoTransactionRule ¶ added in v1.4.2
type MultiWriteNoTransactionRule struct {
*rules.BaseRule
// contains filtered or unexported fields
}
MultiWriteNoTransactionRule detects a function that changes persistent state more than once without a transaction around the changes.
The failure is not hypothetical and it is not loud. Each write succeeds on its own, so nothing in the logs says "half of this operation happened". The record simply disagrees with itself from then on, and the disagreement is found later, by a human, in money.
Real case (ProjectA, 2026-07-31). Completing a withdrawal wrote four rows in sequence: the transaction hash onto the request, a posting into `transactions`, the request's status to `completed`, and finally the ledger entries in `fund_transfers`. The last step was allowed to fail — the code even carried a comment saying rollback was impossible at that point. A failure there left the withdrawal marked completed, with a posting under it, and no ledger entry: the money was recorded as gone and unaccounted for at the same time. The four writes are now one transaction, and the regression test asserts that a failing last step leaves the request in its previous status.
What is reported: two or more calls, with distinct method names, that mutate a store (a receiver whose type name reads as a repository, store or DAO) and that can both run in the same pass through the function. What is not reported:
- Calls in mutually exclusive arms of the same if or switch — only one of them runs.
- A retry of the same call: the same method name twice is one write attempted twice.
- Writes inside a goroutine body: that work outlives the function and cannot share its transaction. Project spawners that hide the `go` inside a helper, and telemetry that must survive precisely when the business operation fails, are listed in independent_calls.
- Writes already inside a transaction runner's callback, and functions that are only ever reached from inside one — the wrapper does not have to sit in the same function as the writes, and requiring that would push every helper back into one long method.
Deliberately separate steps do exist: crediting a deposit and auto-investing it are two operations, and rolling back the credit because the investment failed would be worse than leaving them apart. Such a pair is exempted by naming the runner in transaction_functions only if it truly runs under one, so the honest way to silence this rule for a deliberate split is a comment on the function and a rule exclusion, not a fake transaction.
func NewMultiWriteNoTransactionRule ¶ added in v1.4.2
func NewMultiWriteNoTransactionRule() *MultiWriteNoTransactionRule
NewMultiWriteNoTransactionRule creates the rule.
func (*MultiWriteNoTransactionRule) AnalyzeFile ¶ added in v1.4.2
func (r *MultiWriteNoTransactionRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
AnalyzeFile is a no-op: whether a helper already runs inside a transaction is decided by its callers, which live in other files.
func (*MultiWriteNoTransactionRule) AnalyzeGoProject ¶ added in v1.4.2
func (r *MultiWriteNoTransactionRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
AnalyzeGoProject collects the functions that run under a transaction, then reports the rest.
func (*MultiWriteNoTransactionRule) Configure ¶ added in v1.4.2
func (r *MultiWriteNoTransactionRule) Configure(settings map[string]any) error
Configure accepts overrides for what counts as a store and as a transaction runner.
func (*MultiWriteNoTransactionRule) RequiresSSA ¶ added in v1.4.2
func (r *MultiWriteNoTransactionRule) RequiresSSA() bool
RequiresSSA reports that typed packages are enough.
type MutexLockRule ¶
MutexLockRule detects mutex Lock() without corresponding Unlock()
func (*MutexLockRule) AnalyzeFile ¶
func (r *MutexLockRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for mutex lock without defer unlock
type NilDIRule ¶ added in v1.4.2
NilDIRule detects nil arguments passed to constructor functions (New*) which often indicates missing dependency injection configuration. Focuses on high-risk parameters: logger, service, repo, storage, handler.
func (*NilDIRule) AnalyzeFile ¶ added in v1.4.2
func (r *NilDIRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for nil arguments in constructor calls
type NilSliceRule ¶
NilSliceRule detects nil slice comparisons and returns
func (*NilSliceRule) AnalyzeFile ¶
func (r *NilSliceRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for nil slice comparisons
type NonAtomicStatusHistoryRule ¶ added in v1.4.2
NonAtomicStatusHistoryRule detects status mutations followed by a separate history write on the same repository in one function.
func NewNonAtomicStatusHistoryRule ¶ added in v1.4.2
func NewNonAtomicStatusHistoryRule() *NonAtomicStatusHistoryRule
NewNonAtomicStatusHistoryRule creates the rule.
func (*NonAtomicStatusHistoryRule) AnalyzeFile ¶ added in v1.4.2
func (r *NonAtomicStatusHistoryRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks each production Go function independently.
type NonCanonicalLoggerRule ¶ added in v1.4.2
NonCanonicalLoggerRule detects usage of non-canonical logging in production code.
Rationale: projects that standardize on a single logger (slog, zerolog, or a project-local canonical_logger) need every production code path to route diagnostics through that logger so that formatting, sampling and destinations stay consistent. Ad-hoc calls to log.Printf, fmt.Print* or parallel logger libraries (zap, logrus) bypass that pipeline.
Detects:
- Calls to log.Printf/Println/Print/Fatal/Panic and their formatted variants
- fmt.Print/Println/Printf used as diagnostic output (not as error construction)
- Imports of known parallel logger libraries (zap, logrus, glog, zerolog) in projects where they are not the canonical choice
Skips:
- Test files (*_test.go, /tests/, /testdata/)
- cmd/**/main.go (CLI entry points can use bare fmt/log)
- Files explicitly configured as exceptions in .glint.yaml
func NewNonCanonicalLoggerRule ¶ added in v1.4.2
func NewNonCanonicalLoggerRule() *NonCanonicalLoggerRule
NewNonCanonicalLoggerRule creates the rule
func (*NonCanonicalLoggerRule) AnalyzeFile ¶ added in v1.4.2
func (r *NonCanonicalLoggerRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for non-canonical logger usage
type NullableObjectCallRule ¶ added in v1.4.2
NullableObjectCallRule detects Object.* calls on nested values that may be null/undefined in API responses. These calls throw at runtime when the target is not an object.
func NewNullableObjectCallRule ¶ added in v1.4.2
func NewNullableObjectCallRule() *NullableObjectCallRule
NewNullableObjectCallRule creates the rule
func (*NullableObjectCallRule) AnalyzeFile ¶ added in v1.4.2
func (r *NullableObjectCallRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for Object.* calls on possibly nullable values
type OrphanedInterfaceRule ¶ added in v1.4.2
OrphanedInterfaceRule detects interfaces with no implementations or usages These are "dead code" interfaces that can be safely removed
func NewOrphanedInterfaceRule ¶ added in v1.4.2
func NewOrphanedInterfaceRule() *OrphanedInterfaceRule
NewOrphanedInterfaceRule creates the rule
func (*OrphanedInterfaceRule) AnalyzeFile ¶ added in v1.4.2
func (r *OrphanedInterfaceRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for orphaned interfaces
type PaginationBoundaryTruncationRule ¶ added in v1.4.2
PaginationBoundaryTruncationRule detects silent page caps on time-bounded history walks.
func NewPaginationBoundaryTruncationRule ¶ added in v1.4.2
func NewPaginationBoundaryTruncationRule() *PaginationBoundaryTruncationRule
NewPaginationBoundaryTruncationRule creates the rule.
func (*PaginationBoundaryTruncationRule) AnalyzeFile ¶ added in v1.4.2
func (r *PaginationBoundaryTruncationRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile finds loops with both a temporal completion boundary and a silent page-cap break.
type ProviderCommandBeforeIntentPersistRule ¶ added in v1.4.2
ProviderCommandBeforeIntentPersistRule detects financial provider commands executed before their durable request or intent is recorded.
func NewProviderCommandBeforeIntentPersistRule ¶ added in v1.4.2
func NewProviderCommandBeforeIntentPersistRule() *ProviderCommandBeforeIntentPersistRule
NewProviderCommandBeforeIntentPersistRule creates the rule.
func (*ProviderCommandBeforeIntentPersistRule) AnalyzeFile ¶ added in v1.4.2
func (r *ProviderCommandBeforeIntentPersistRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks command and persistence ordering within each function.
type ProviderCommandRetryRule ¶ added in v1.4.2
ProviderCommandRetryRule detects automatic retries of destructive provider commands.
func NewProviderCommandRetryRule ¶ added in v1.4.2
func NewProviderCommandRetryRule() *ProviderCommandRetryRule
NewProviderCommandRetryRule creates the rule.
func (*ProviderCommandRetryRule) AnalyzeFile ¶ added in v1.4.2
func (r *ProviderCommandRetryRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks same-file helper signatures, retry callbacks, and loops.
type QuadraticLoopRule ¶ added in v1.4.2
QuadraticLoopRule detects work that grows with the square of the input:
for i := range windows { // every window…
for j := range windows { // …compared with every other one
for strings.Contains(line, " ") { // scan the whole string…
line = strings.ReplaceAll(line, " ", " ") // …after each replacement
Both shapes are correct and stay fast on the examples they were written against; they turn into minutes once the input grows. Both cost glint itself dearly: the nested window comparison made a 900-file project take over two minutes, and the rescanning replace was the hot spot of line normalization.
Not flagged: loops over two different collections (O(n*m) is what the code asks for), an inner loop over a field of the outer element, and a body that does nothing but count.
func NewQuadraticLoopRule ¶ added in v1.4.2
func NewQuadraticLoopRule() *QuadraticLoopRule
NewQuadraticLoopRule creates the rule
func (*QuadraticLoopRule) AnalyzeFile ¶ added in v1.4.2
func (r *QuadraticLoopRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile looks for the two quadratic shapes in one file.
type QueryInLoopRule ¶ added in v1.4.2
QueryInLoopRule detects repository/DB method calls inside loops (N+1 query anti-pattern). Это правило родилось из REF-312: per-итерационные SQL-вызовы (config_timeline.Resolve, GetNetFundTransfersToVault) давали ~9000 запросов и 9.5s на список клиентов.
func NewQueryInLoopRule ¶ added in v1.4.2
func NewQueryInLoopRule() *QueryInLoopRule
NewQueryInLoopRule creates the rule.
func (*QueryInLoopRule) AnalyzeFile ¶ added in v1.4.2
func (r *QueryInLoopRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile walks loops and flags data-access calls directly inside them.
type RangeValPointerRule ¶
RangeValPointerRule detects taking address of range loop variable
func NewRangeValPointerRule ¶
func NewRangeValPointerRule() *RangeValPointerRule
NewRangeValPointerRule creates the rule
func (*RangeValPointerRule) AnalyzeFile ¶
func (r *RangeValPointerRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for pointer to range variable
type ReactRemountKeyRule ¶ added in v1.4.2
ReactRemountKeyRule detects a JSX key built from a value that a controlled input inside the same element edits. React treats a changed key as a new element: every keystroke unmounts the subtree, the fresh input mounts empty of focus, and the user can type exactly one character at a time.
Real case (projectB, 2026-08-05): a wallet row used key={`${wallet.walletAddress}-${index}`} while its <input value={wallet.walletAddress} onChange=.../> edited that very address.
Precision over recall: the rule fires only when the key expression and the input's value= provably reference the same dotted path (x.field) and the input has an onChange/onInput handler in the same tag.
func NewReactRemountKeyRule ¶ added in v1.4.2
func NewReactRemountKeyRule() *ReactRemountKeyRule
NewReactRemountKeyRule creates the rule.
func (*ReactRemountKeyRule) AnalyzeFile ¶ added in v1.4.2
func (r *ReactRemountKeyRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks TSX/JSX sources.
type RedundantCompatibilityRule ¶ added in v1.4.2
type RedundantCompatibilityRule struct {
*rules.BaseRule
// contains filtered or unexported fields
}
RedundantCompatibilityRule detects false backward compatibility patterns This implements CLAUDE.md principles: - "Delete cleanly, git remembers" - no fake compatibility - SRP - one way to do one thing Catches: - Multiple context key fallbacks (GetAdminIDFromContext checking 3 different keys) - False "backward compatibility" comments without external API consumers - Duplicate key definitions (AdminIDKey vs AdminIDKeyAlt)
func NewRedundantCompatibilityRule ¶ added in v1.4.2
func NewRedundantCompatibilityRule() *RedundantCompatibilityRule
NewRedundantCompatibilityRule creates the rule
func (*RedundantCompatibilityRule) AnalyzeFile ¶ added in v1.4.2
func (r *RedundantCompatibilityRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for redundant compatibility patterns
type ReimplementedStdlibRule ¶ added in v1.4.2
ReimplementedStdlibRule detects small helpers that redo what the standard library already does:
func itoa(i int) string { // strconv.Itoa
s := ""
for i > 0 { s = string(rune('0'+i%10)) + s; i /= 10 }
return s
}
The copy is not merely redundant: it is the version nobody tested. glint carried four such itoa helpers, and they all shared the same bug — a negative number came back as the empty string.
The rule recognizes the shapes it can name with certainty: digit-by-digit integer formatting and parsing, a linear search for an element, absolute value, the smaller/larger of two values, and reversing a slice in place.
func NewReimplementedStdlibRule ¶ added in v1.4.2
func NewReimplementedStdlibRule() *ReimplementedStdlibRule
NewReimplementedStdlibRule creates the rule
func (*ReimplementedStdlibRule) AnalyzeFile ¶ added in v1.4.2
func (r *ReimplementedStdlibRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks every function declared in the file.
type ResponseTypeInFunctionRule ¶ added in v1.4.2
ResponseTypeInFunctionRule detects a wire contract declared inside a function body.
A struct with json tags that a function builds and hands to a response writer is an API contract, but declaring it in function scope hides it from every consumer: type generators cannot emit it, other handlers cannot reuse it, and clients end up hand-copying the field list. Each hand-made copy then drifts on its own.
Real case (ProjectA, 2026-07-29): the dashboard response type lived inside the handler, so the Go→TypeScript generator never saw it and the frontend grew three hand-written copies with different field sets. Two screens read different copies and showed different balances under the same label.
Only produced contracts are reported. A local struct used to decode a request body is a normal Go idiom: it is filled by the decoder, never composed field by field, so it does not match.
func NewResponseTypeInFunctionRule ¶ added in v1.4.2
func NewResponseTypeInFunctionRule() *ResponseTypeInFunctionRule
NewResponseTypeInFunctionRule creates the rule
func (*ResponseTypeInFunctionRule) AnalyzeFile ¶ added in v1.4.2
func (r *ResponseTypeInFunctionRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile reports function-local structs with json tags that are sent as a response.
type ReturnNilErrorRule ¶
ReturnNilErrorRule detects functions returning (nil, nil) which is often a bug
func NewReturnNilErrorRule ¶
func NewReturnNilErrorRule() *ReturnNilErrorRule
NewReturnNilErrorRule creates the rule
func (*ReturnNilErrorRule) AnalyzeFile ¶
func (r *ReturnNilErrorRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for (nil, nil) returns
type SQLRowsCloseRule ¶
SQLRowsCloseRule detects SQL rows not being closed
func NewSQLRowsCloseRule ¶
func NewSQLRowsCloseRule() *SQLRowsCloseRule
NewSQLRowsCloseRule creates the rule
func (*SQLRowsCloseRule) AnalyzeFile ¶
func (r *SQLRowsCloseRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for unclosed SQL rows
type ScatteredConstructionRule ¶ added in v1.4.2
ScatteredConstructionRule detects struct types that are constructed via struct literals in too many different functions. Each construction site is a potential point of failure when a new field is added — the field will be silently missing in all but the updated sites.
Principle: "One conversion function per type pair, not scattered literals"
func NewScatteredConstructionRule ¶ added in v1.4.2
func NewScatteredConstructionRule() *ScatteredConstructionRule
NewScatteredConstructionRule creates the rule
func (*ScatteredConstructionRule) AnalyzeFile ¶ added in v1.4.2
func (r *ScatteredConstructionRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for struct types constructed in too many places
func (*ScatteredConstructionRule) Configure ¶ added in v1.4.2
func (r *ScatteredConstructionRule) Configure(settings map[string]any) error
Configure allows setting rule options
func (*ScatteredConstructionRule) ResetState ¶ added in v1.4.2
func (r *ScatteredConstructionRule) ResetState()
ResetState clears the construction sites collected so far. The rule accumulates them across files, so without a reset a second project root would inherit the sites of the first one.
type SelectStarStructScanRule ¶ added in v1.4.2
SelectStarStructScanRule detects `SELECT *` against a real table in Go SQL literals.
Родилось из REF-410. sqlx без Unsafe() требует, чтобы каждой колонке ответа нашлось поле в структуре назначения. Поэтому `SELECT *` привязывает чтение к текущему набору колонок: миграция, добавляющая колонку, ломает выборку на рантайме с "missing destination name", хотя Go-код не менялся и сборка прошла. Явный список колонок снимает эту связь.
Производные таблицы (`SELECT * FROM (...) t`) правилом не считаются нарушением: там звёздочка берёт колонки подзапроса, а их набор задан тут же в коде.
func NewSelectStarStructScanRule ¶ added in v1.4.2
func NewSelectStarStructScanRule() *SelectStarStructScanRule
NewSelectStarStructScanRule creates the rule.
func (*SelectStarStructScanRule) AnalyzeFile ¶ added in v1.4.2
func (r *SelectStarStructScanRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile walks string literals looking for SELECT * over a table.
type SelectThenWriteRaceRule ¶ added in v1.4.2
SelectThenWriteRaceRule detects a read-validate-write race inside one function: a SELECT of a column without FOR UPDATE followed by an UPDATE of the same column of the same table. Two concurrent calls read the same value, both pass the validation performed between the queries, and the second one silently overwrites the first.
Real case (projectB, 2026-08-05): status transitions read `status`, ran a state-machine check on the value, then wrote `status` back. Concurrent sync-materializer and manual close both read the same status and both passed the transition validation.
The rule is silent when the SELECT locks the row (FOR UPDATE / FOR NO KEY UPDATE / FOR SHARE) — that is exactly the fix.
func NewSelectThenWriteRaceRule ¶ added in v1.4.2
func NewSelectThenWriteRaceRule() *SelectThenWriteRaceRule
NewSelectThenWriteRaceRule creates the rule.
func (*SelectThenWriteRaceRule) AnalyzeFile ¶ added in v1.4.2
func (r *SelectThenWriteRaceRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks each function's SQL literals for the read-then-write pattern.
type ShadowVariableRule ¶
ShadowVariableRule detects variable shadowing
func NewShadowVariableRule ¶
func NewShadowVariableRule() *ShadowVariableRule
NewShadowVariableRule creates the rule
func (*ShadowVariableRule) AnalyzeFile ¶
func (r *ShadowVariableRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for variable shadowing
type SilentConfigErrorRule ¶ added in v1.4.2
SilentConfigErrorRule detects silent error ignoring in config/bootstrap code. CLAUDE.md: "Fail explicitly, never degrade silently" — absolute rule; in config/env-loading paths it is critical because misconfiguration stays invisible until a much later failure.
Unlike the generic `ignored-error` rule, this one intentionally does NOT honor `//nolint:errcheck` — policy forbids the pattern regardless of the linter suppression.
Detects in files whose path contains `/config/` OR matches `**/bootstrap_environment_loader.go` / `**/unified_config*.go`:
- `_ = X()` where X is an env/config-loading call (ReadEnv, Load, loadEnvFileDirectly, godotenv.Load, cleanenv.ReadConfig, etc.)
- Bare call `X()` whose error return is dropped entirely (no assignment) for the same set of env/config functions.
Skips:
- Test files / test utility files
- Generated files
func NewSilentConfigErrorRule ¶ added in v1.4.2
func NewSilentConfigErrorRule() *SilentConfigErrorRule
NewSilentConfigErrorRule creates the rule
func (*SilentConfigErrorRule) AnalyzeFile ¶ added in v1.4.2
func (r *SilentConfigErrorRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for silent config errors
func (*SilentConfigErrorRule) SuppressionExempt ¶ added in v1.4.2
func (r *SilentConfigErrorRule) SuppressionExempt() bool
SuppressionExempt reports that policy forbids silent config errors unconditionally, so inline nolint/safe comments do not silence this rule.
type SilentErrorHandlingRule ¶ added in v1.4.2
SilentErrorHandlingRule detects error checks that don't log or propagate the error This implements CLAUDE.md principle: "Log all errors, never ignore silently" Catches: if err != nil { return X } without logging or returning the error
func NewSilentErrorHandlingRule ¶ added in v1.4.2
func NewSilentErrorHandlingRule() *SilentErrorHandlingRule
NewSilentErrorHandlingRule creates the rule
func (*SilentErrorHandlingRule) AnalyzeFile ¶ added in v1.4.2
func (r *SilentErrorHandlingRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for silent error handling
type SilentlyOptionalDependencyRule ¶ added in v1.4.2
SilentlyOptionalDependencyRule detects a dependency that is injected by a setter, whose absence silently switches a feature off, and which at least one construction site cannot have received.
Родилось из реального инцидента (REF-446). У сервиса расчёта доходности был метод SetAnomalyAlerter, а в детекторе аномалий стояло `if s.anomalyAlerter == nil { return }`. Сервис собирался в девяти местах, сеттер звали в восьми — и алерты по аномалиям доходности не ушли ни разу за всю историю прода. Ничего не падало и не логировалось: фича просто отсутствовала на том экземпляре, который считал ночной пересчёт.
Признаков нужно три сразу, и по отдельности ни один из них не проблема:
- зависимость приходит сеттером, а не в конструктор — её можно не задать;
- её отсутствие проверяется молчаливым `return` — никто не узнает, что не задали;
- точек сборки больше, чем вызовов сеттера — значит хотя бы одна осталась без него.
Третий признак и делает правило точным: сервис с единственной точкой сборки собран правильно, и трогать его незачем. Считается по всему проекту, тестовые файлы не в счёт — там конструируют без зависимостей намеренно.
func NewSilentlyOptionalDependencyRule ¶ added in v1.4.2
func NewSilentlyOptionalDependencyRule() *SilentlyOptionalDependencyRule
NewSilentlyOptionalDependencyRule creates the rule.
func (*SilentlyOptionalDependencyRule) AnalyzeFile ¶ added in v1.4.2
func (r *SilentlyOptionalDependencyRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
AnalyzeFile does nothing: the rule needs the whole project to count construction sites.
func (*SilentlyOptionalDependencyRule) AnalyzeGoProject ¶ added in v1.4.2
func (r *SilentlyOptionalDependencyRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
AnalyzeGoProject pairs setters with silent guards, then counts construction sites.
func (*SilentlyOptionalDependencyRule) RequiresSSA ¶ added in v1.4.2
func (r *SilentlyOptionalDependencyRule) RequiresSSA() bool
RequiresSSA reports that typed packages are enough — no SSA program needed.
type SleepWithoutContextRule ¶ added in v1.4.2
SleepWithoutContextRule detects time.Sleep inside a function that has a context.Context available (as a parameter or captured from the enclosing function). Cancelling the context does not interrupt the pause: a sync loop sleeping 250ms per item keeps running long after the caller gave up, and a graceful shutdown waits out every pending sleep.
Real case (projectB, 2026-08-05): wallet sync slept 200ms between provider APIs and 250ms per Solana transaction with a live ctx in scope; stopping the sync had to wait for the whole backlog of pauses.
func NewSleepWithoutContextRule ¶ added in v1.4.2
func NewSleepWithoutContextRule() *SleepWithoutContextRule
NewSleepWithoutContextRule creates the rule.
func (*SleepWithoutContextRule) AnalyzeFile ¶ added in v1.4.2
func (r *SleepWithoutContextRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile flags time.Sleep calls that ignore an available context.
type StringConcatRule ¶
StringConcatRule detects string concatenation in loops
func NewStringConcatRule ¶
func NewStringConcatRule() *StringConcatRule
NewStringConcatRule creates the rule
func (*StringConcatRule) AnalyzeFile ¶
func (r *StringConcatRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for string concatenation in loops
type TautologicalAssertionRule ¶ added in v1.4.2
TautologicalAssertionRule detects tests that cannot fail.
A green suite is trusted, so an assertion that holds no matter what the code does is worse than no test at all: it claims coverage of exactly the behaviour nobody is watching.
Three shapes, all seen in production repositories:
- both sides of an equality assertion are the same expression;
- the asserted value is a literal constant (expect(true).toBe(true));
- the assertion sits inside a condition derived from the same value, so it skips itself precisely when the value would have been interesting.
Real case (ProjectA, 2026-07-29): a regression test named "withdrawal and analytics headline numbers stay in lockstep" compared displayedBalance with displayedBalance and documented a contract the code did not have. It stayed green while the two screens drifted apart and started showing different balances.
func NewTautologicalAssertionRule ¶ added in v1.4.2
func NewTautologicalAssertionRule() *TautologicalAssertionRule
NewTautologicalAssertionRule creates the rule
func (*TautologicalAssertionRule) AnalyzeFile ¶ added in v1.4.2
func (r *TautologicalAssertionRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile inspects test files only: an assertion outside a test is not an assertion.
type TechDebtRule ¶
TechDebtRule detects technical debt patterns beyond simple TODO comments
func (*TechDebtRule) AnalyzeFile ¶
func (r *TechDebtRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for tech debt patterns
type TerminalAfterFailedCheckpointRule ¶ added in v1.4.2
TerminalAfterFailedCheckpointRule detects terminal success after a durable checkpoint failure was ignored.
func NewTerminalAfterFailedCheckpointRule ¶ added in v1.4.2
func NewTerminalAfterFailedCheckpointRule() *TerminalAfterFailedCheckpointRule
NewTerminalAfterFailedCheckpointRule creates the rule.
func (*TerminalAfterFailedCheckpointRule) AnalyzeFile ¶ added in v1.4.2
func (r *TerminalAfterFailedCheckpointRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks each production Go function independently.
type TestExternalServiceRule ¶ added in v1.4.2
TestExternalServiceRule detects tests that reach a live third-party service.
A test exists to check our own code. Calling someone else's API from a test buys nothing — the vendor is not under our control and their behaviour is not our contract — and it costs three things: the suite starts failing on their outages and quotas, the vendor's rate limits turn into flaky red builds, and, worst, the test changes the outside world.
Real case (ProjectA, 2026-07-30). Four tests talked to production APIs on every `make test-all`: two of them took real deposit requisites from the payment provider cryptoprov, two burned paid ExtVault Pro credits. Over one week the suite consumed 537 provider addresses. The failures looked like flaky parallelism — the provider starts answering 451 after roughly ten allocations per window, so the more workers, the more red.
Two shapes are reported.
outbound_client: a test calls into a package that both talks HTTP and carries a literal third-party URL. Packages that merely hold configuration, or clients aimed at localhost, do not qualify — the package must actually make outbound requests.
credential_gate: a test guards itself with "skip unless the credential is present". That is not a guard at all. Credentials live in .env, and test runners export it (`set -a && source .env`), so the gate is open in exactly the environment where the test runs. Both ProjectA vault tests carried `if os.Getenv("EXTVAULT_ACCESS_KEY") == "" { t.Skip() }` and were documented as manual; they ran every single time. An opt-in must be a switch nobody sets by accident, not a secret everybody has.
A test that genuinely needs the live service stays possible: name its opt-in helper in the guard_functions setting, and calls guarded by it are not reported.
func NewTestExternalServiceRule ¶ added in v1.4.2
func NewTestExternalServiceRule() *TestExternalServiceRule
NewTestExternalServiceRule creates the rule
func (*TestExternalServiceRule) AnalyzeFile ¶ added in v1.4.2
func (r *TestExternalServiceRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
AnalyzeFile is a no-op: deciding whether a called package talks to the outside world needs the whole project, not one file.
func (*TestExternalServiceRule) AnalyzeGoProject ¶ added in v1.4.2
func (r *TestExternalServiceRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
AnalyzeGoProject marks packages that make outbound calls, then looks for tests reaching them.
func (*TestExternalServiceRule) Configure ¶ added in v1.4.2
func (r *TestExternalServiceRule) Configure(settings map[string]any) error
Configure accepts the list of opt-in helpers that legitimise a live call.
func (*TestExternalServiceRule) RequiresSSA ¶ added in v1.4.2
func (r *TestExternalServiceRule) RequiresSSA() bool
RequiresSSA reports that plain typed packages are enough — no SSA program needed.
type TestSchemaMutationWithoutCleanupRule ¶ added in v1.4.2
type TestSchemaMutationWithoutCleanupRule struct {
*rules.BaseRule
// contains filtered or unexported fields
}
TestSchemaMutationWithoutCleanupRule detects a test that changes the database schema and leaves the change behind.
Тестовые базы обычно переиспользуются между прогонами (пул с TRUNCATE, шаблонная база, общий контейнер). Строки такой тест за собой чистит, а колонку или таблицу — нет: TRUNCATE структуру не трогает. Дальше своя же колонка ломает следующий прогон («column already exists»), а чужие тесты получают базу, не совпадающую со схемой из миграций, и падают в стороне от причины.
Правило требует, чтобы рядом с DDL стояла отмена: t.Cleanup или defer. Что именно там написано, правило не проверяет — важно, что автор про возврат схемы подумал.
func NewTestSchemaMutationWithoutCleanupRule ¶ added in v1.4.2
func NewTestSchemaMutationWithoutCleanupRule() *TestSchemaMutationWithoutCleanupRule
NewTestSchemaMutationWithoutCleanupRule creates the rule.
func (*TestSchemaMutationWithoutCleanupRule) AnalyzeFile ¶ added in v1.4.2
func (r *TestSchemaMutationWithoutCleanupRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile reports test functions that run DDL with no undo registered.
type TestWithoutAssertionRule ¶ added in v1.4.2
TestWithoutAssertionRule detects Go test functions that cannot fail because they never assert anything — typically "documentation" tests that print a finding and stay green forever:
func TestOverflowProtection(t *testing.T) {
result := maxInt64.Mul(million)
t.Logf("VULNERABILITY: No overflow detection on Mul()")
}
Such a test claims coverage of behaviour nobody verifies. Either assert what the code must do, or delete the test.
The give-away is t.Log/t.Logf standing where an assertion belongs: the test states a finding instead of checking it. A test that merely exercises code without logging is a different, legitimate thing — it fails if that code panics — and is not reported.
Not flagged: tests that assert via testify/t.Error/t.Fatal, tests that hand *testing.T to a helper (the helper asserts), tests whose only statement is a compile-time assertion (`var _ Iface = (*Impl)(nil)` — the compiler enforces it), smoke calls without logging, skipped tests (see skipped-tests), TestMain, benchmarks and fuzz targets.
Companion rules: unfalsifiable-test-case covers TS/JS tests whose assertions hold regardless of behaviour; tautological-assertion covers `require.True(t, true)`. This one covers the "no assertion at all" case in Go.
func NewTestWithoutAssertionRule ¶ added in v1.4.2
func NewTestWithoutAssertionRule() *TestWithoutAssertionRule
NewTestWithoutAssertionRule creates the rule
func (*TestWithoutAssertionRule) AnalyzeFile ¶ added in v1.4.2
func (r *TestWithoutAssertionRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks Go test functions for the missing-assertion pattern
type TimeEqualRule ¶
TimeEqualRule detects time.Time comparisons using == instead of .Equal()
func (*TimeEqualRule) AnalyzeFile ¶
func (r *TimeEqualRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for time.Time == comparisons
type TodoCommentRule ¶
TodoCommentRule finds actionable TODO/FIXME/HACK/XXX comments in code
func NewTodoCommentRule ¶
func NewTodoCommentRule() *TodoCommentRule
NewTodoCommentRule creates the rule
func (*TodoCommentRule) AnalyzeFile ¶
func (r *TodoCommentRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile finds actionable task comments
type TombstoneCommentRule ¶ added in v1.4.2
TombstoneCommentRule detects "tombstone" comments describing code that was deleted:
// GetDB removed — architectural boundary violation eliminated // УДАЛЕНО: processed статус (дубликат approved) _ = disableFixes // CryptoProv fixes removed
CLAUDE.md: "Delete cleanly, git remembers" — history lives in git, not in comments. A tombstone is noise the moment the commit lands.
Not flagged: behavior descriptions ("entries are removed after TTL"), godoc deprecation markers (owned by the deprecated-comment rule), policy quotes.
func NewTombstoneCommentRule ¶ added in v1.4.2
func NewTombstoneCommentRule() *TombstoneCommentRule
NewTombstoneCommentRule creates the rule
func (*TombstoneCommentRule) AnalyzeFile ¶ added in v1.4.2
func (r *TombstoneCommentRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks comment lines for tombstones
type TypeInferrer ¶
type TypeInferrer struct {
// contains filtered or unexported fields
}
TypeInferrer infers types from AST declarations within a file
func NewTypeInferrer ¶
func NewTypeInferrer(file *ast.File) *TypeInferrer
NewTypeInferrer creates a type inferrer and collects type info from the AST
func NewTypeInferrerFromNode ¶ added in v1.4.2
func NewTypeInferrerFromNode(node ast.Node) *TypeInferrer
NewTypeInferrerFromNode creates a type inferrer for a specific AST scope.
func (*TypeInferrer) GetType ¶
func (ti *TypeInferrer) GetType(name string) (TypeInfo, bool)
GetType returns type info for a variable name. Names bound to several types in the same file report no type at all rather than the first one seen.
func (*TypeInferrer) IsAmbiguous ¶ added in v1.4.2
func (ti *TypeInferrer) IsAmbiguous(name string) bool
IsAmbiguous reports whether the file binds this name to more than one type.
func (*TypeInferrer) IsAny ¶ added in v1.4.2
func (ti *TypeInferrer) IsAny(name string) bool
IsAny checks if a variable is any/interface{}. It follows the GetType contract: a name the file binds to several types is ambiguous and reports no type at all — not the first one seen.
func (*TypeInferrer) IsDeclared ¶ added in v1.4.2
func (ti *TypeInferrer) IsDeclared(name string) bool
IsDeclared reports whether the analyzed scope introduces this name at all, regardless of whether its type could be inferred.
type TypeInfo ¶
type TypeInfo struct {
IsSlice bool
IsMap bool
IsTime bool
IsError bool
IsChan bool
TypeName string // e.g., "[]string", "time.Time", "error"
ElementTypeName string
}
TypeInfo holds inferred type information for a variable
type TypedNilIntoInterfaceRule ¶ added in v1.4.2
TypedNilIntoInterfaceRule detects a nil-able concrete pointer handed to an interface.
Указатель, равный nil, уложенный в интерфейс, интерфейсом nil не является: у значения есть тип, поэтому `iface == nil` даёт false. Получатель, который отличает отсутствие зависимости именно этой проверкой, пропускает её и вызывает метод на nil-получателе.
Реальный случай (projectA, REF-446): сервис доходности принял алертер параметром конструктора, и точки сборки стали передавать *email.Service напрямую. Без SMTP это nil-указатель. Проверка `if s.anomalyAlerter == nil { return }` его не поймала, и первый же аномальный день в истории vault уронил пользовательский график баланса паникой вместо того, чтобы просто не отправить письмо.
Правило намеренно узкое, иначе тонет в шуме:
- указатель должен где-то в этом же файле сравниваться с nil — иначе считать его пустым нет оснований;
- получатель должен зависимость сохранять: конструктор, сеттер или присваивание в интерфейсную переменную. Передача в обходчик вроде ast.Inspect не в счёт;
- проверка на nil в Cleanup/Close доказательством не считается: teardown по замыслу переживает частично собранный объект;
- присваивание внутри `if ptr != nil { ... }` и код после `if ptr == nil { return }` признаются правильными — это и есть нужная нормализация.
func NewTypedNilIntoInterfaceRule ¶ added in v1.4.2
func NewTypedNilIntoInterfaceRule() *TypedNilIntoInterfaceRule
NewTypedNilIntoInterfaceRule creates the rule.
func (*TypedNilIntoInterfaceRule) AnalyzeFile ¶ added in v1.4.2
func (r *TypedNilIntoInterfaceRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
AnalyzeFile does nothing: nil-ability evidence is collected across the whole project.
func (*TypedNilIntoInterfaceRule) AnalyzeGoProject ¶ added in v1.4.2
func (r *TypedNilIntoInterfaceRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
AnalyzeGoProject collects nil-checked pointers, then finds where they enter interfaces.
func (*TypedNilIntoInterfaceRule) RequiresSSA ¶ added in v1.4.2
func (r *TypedNilIntoInterfaceRule) RequiresSSA() bool
RequiresSSA reports that typed packages are enough — no SSA program needed.
type UnboundedResponseReadRule ¶ added in v1.4.2
UnboundedResponseReadRule detects unbounded reads of HTTP response bodies.
func NewUnboundedResponseReadRule ¶ added in v1.4.2
func NewUnboundedResponseReadRule() *UnboundedResponseReadRule
NewUnboundedResponseReadRule creates the rule.
func (*UnboundedResponseReadRule) AnalyzeFile ¶ added in v1.4.2
func (r *UnboundedResponseReadRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile checks for unbounded HTTP response body reads.
type UnboundedSyncMapRule ¶ added in v1.4.2
UnboundedSyncMapRule detects package-level sync.Map variables that production code grows (Store/LoadOrStore) but never shrinks (Delete/CompareAndDelete/ Clear). In a long-running process such a map is a slow leak: every new key stays forever.
Родилось из ревью projectD 2026-08 (№22): пакетные sync.Map доменных расписаний, Crawl-delay и состояний robots.txt копили по записи на каждый встреченный домен. Демон работает месяцами — набор доменов только растёт, вытеснения не было ни в одном файле пакета.
Не считаются: локальные sync.Map (живут не дольше функции), карты без записей и карты, у которых есть хоть одно не-методное использование (передача указателя наружу — судьбу записей отсюда не видно). Delete только в _test.go не спасает: production-рост он не ограничивает.
func NewUnboundedSyncMapRule ¶ added in v1.4.2
func NewUnboundedSyncMapRule() *UnboundedSyncMapRule
NewUnboundedSyncMapRule creates the rule.
func (*UnboundedSyncMapRule) AnalyzeFile ¶ added in v1.4.2
func (r *UnboundedSyncMapRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
AnalyzeFile does nothing: the rule needs the whole package to see eviction.
func (*UnboundedSyncMapRule) AnalyzeGoProject ¶ added in v1.4.2
func (r *UnboundedSyncMapRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
AnalyzeGoProject inspects every package for grow-only package-level sync.Maps.
func (*UnboundedSyncMapRule) RequiresSSA ¶ added in v1.4.2
func (r *UnboundedSyncMapRule) RequiresSSA() bool
RequiresSSA reports that typed packages are enough — no SSA program needed.
type UnfalsifiableTestCaseRule ¶ added in v1.4.2
UnfalsifiableTestCaseRule detects a browser/API test whose every assertion holds no matter what the code under test does.
Родилось из разбора e2e-набора projectA (REF-410/REF-468). Два спека «проверяли» показ балансов так: мокали два адреса, которых на бэкенде не существует, шли на страницу, которой в приложении нет, и утверждали «URL содержит deposits, body виден, элементов больше нуля». Они годами проходили против страницы 404 и считались покрытием. Ещё несколько спеков писали expect([200, 404]).toContain(status) — при таком наборе одинаково засчитываются и рабочий эндпоинт, и удалённый.
Каждое из таких утверждений по отдельности бывает уместно как разогрев. Признак проблемы в том, что в тесте нет ни одного другого: тогда тест не может упасть и сообщает только о том, что фронтенд отдал HTML.
Правило работает по TypeScript и JavaScript тестам. Go-тесты закрывает tautological-assertion.
func NewUnfalsifiableTestCaseRule ¶ added in v1.4.2
func NewUnfalsifiableTestCaseRule() *UnfalsifiableTestCaseRule
NewUnfalsifiableTestCaseRule creates the rule.
func (*UnfalsifiableTestCaseRule) AnalyzeFile ¶ added in v1.4.2
func (r *UnfalsifiableTestCaseRule) AnalyzeFile(ctx *core.FileContext) []*core.Violation
AnalyzeFile walks each test body and reports the ones with no falsifiable assertion.
type UnguardedSharedFieldRule ¶ added in v1.4.2
type UnguardedSharedFieldRule struct {
}
UnguardedSharedFieldRule detects a field that some methods protect with a mutex and others touch without it:
func (c *Counter) Add(n int) { c.mu.Lock(); defer c.mu.Unlock(); c.value += n }
func (c *Counter) Reset() { c.value = 0 } // same field, no lock
The lock proves the field is shared between goroutines; the method that skips it is a data race. Unlike a missing Unlock it breaks nothing locally, and the race detector only reports it when two goroutines happen to collide during a test — so it survives review and CI and fails in production.
A lock covers the object that owns it, at any depth: s.metrics.mu.Lock() guards s.metrics.total, not s.other.
Not flagged: fields no lock ever covers (they may be set once and only read afterwards), helpers called from inside a critical section, methods whose name promises the caller holds the lock (…Locked, …NoLock, …Unsafe), and plain functions such as constructors, where the value is not shared yet.
func NewUnguardedSharedFieldRule ¶ added in v1.4.2
func NewUnguardedSharedFieldRule() *UnguardedSharedFieldRule
NewUnguardedSharedFieldRule creates the rule
func (*UnguardedSharedFieldRule) AnalyzeFile ¶ added in v1.4.2
func (r *UnguardedSharedFieldRule) AnalyzeFile(_ *core.FileContext) []*core.Violation
AnalyzeFile is a no-op: the methods of a type may live in several files.
func (*UnguardedSharedFieldRule) AnalyzeGoProject ¶ added in v1.4.2
func (r *UnguardedSharedFieldRule) AnalyzeGoProject(ctx *core.GoProjectContext) ([]*core.Violation, error)
AnalyzeGoProject compares, for every field a lock covers somewhere, the places that take the lock with the places that do not.
func (*UnguardedSharedFieldRule) RequiresSSA ¶ added in v1.4.2
func (r *UnguardedSharedFieldRule) RequiresSSA() bool
RequiresSSA reports that typed syntax is enough for this rule.
Source Files
¶
- anon_interface_degradation.go
- append_assign.go
- ast_helpers.go
- audit_actor_propagation.go
- bool_compare.go
- constructor_nil_return.go
- constructor_swallows_nil_dep.go
- context_background.go
- context_first.go
- defer_in_loop.go
- deprecated_ioutil.go
- deprecated_nginx_http2_listen.go
- deterministic_uuid.go
- e2e_blind_wait.go
- empty_block.go
- empty_struct_return.go
- error_length_check.go
- error_masked_as_false_bool.go
- error_masking.go
- error_string.go
- error_string_compare.go
- error_wrap.go
- fallback_return.go
- financial_constants.go
- financial_decimal_float.go
- financial_fp_rounding.go
- financial_json_float.go
- financial_rounded_delta.go
- flow_walker.go
- frontend_env_fallback.go
- frontend_money_arithmetic.go
- frontend_silent_catch.go
- go_modern.go
- http_body_close.go
- idempotency_check_then_create.go
- ignored_error.go
- legacy_comment_marker.go
- legacy_identifier.go
- log_and_return_zero.go
- magic_number.go
- main_return_after_error.go
- map_iteration_order.go
- masked_error_or_condition.go
- migration_duplicate_version.go
- mock_identifier.go
- multi_write_no_transaction.go
- mutex_calls.go
- mutex_lock.go
- nil_di.go
- nil_slice.go
- non_atomic_status_history.go
- non_canonical_logger.go
- nullable_object_call.go
- orphaned_interface.go
- pagination_boundary_truncation.go
- provider_command_before_intent_persist.go
- provider_command_retry.go
- quadratic_loop.go
- query_in_loop.go
- range_val_pointer.go
- react_remount_key.go
- redundant_compatibility.go
- reimplemented_stdlib.go
- response_type_in_function.go
- return_nil_error.go
- scattered_construction.go
- select_star_struct_scan.go
- select_then_write_race.go
- shadow_variable.go
- silent_config_error.go
- silent_error_handling.go
- silently_optional_dependency.go
- skip_paths.go
- sleep_without_context.go
- sql_rows_close.go
- string_concat.go
- tautological_assertion.go
- tech_debt.go
- terminal_after_failed_checkpoint.go
- test_external_service.go
- test_external_service_reach.go
- test_schema_mutation_without_cleanup.go
- test_without_assertion.go
- time_equal.go
- todo_comment.go
- tombstone_comment.go
- type_inference.go
- typed_nil_into_interface.go
- unbounded_response_read.go
- unbounded_sync_map.go
- unfalsifiable_test_case.go
- unguarded_shared_field.go