contractcheck

package
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package contractcheck inspects a project's internal/ tree for contract-shape violations. It is the single home for the analyzers that previously lived as four sibling files under internal/linter/forgeconv/:

  • internal-package-contract-names (Service/Deps/New canonical shape)
  • interactor-deps-are-interfaces (interactor Deps fields are interfaces)
  • adapter-no-rpc (adapter packages don't register RPC)
  • utility-package auto-skip (pre-rule filter for the contract-names rule)

The forge entry points that used to call those analyzers individually (`preCodegenContractCheck` in internal/cli/generate.go and `runConventionLint` in internal/cli/lint.go) now route through one Inspect function. Per-call-site differences — which rules to run, what to exclude — are expressed as fields on Options, not as branches inside the engine.

API design notes

Findings carry the same shape as the other forge lint rules (forgeconv.Finding) so they can be folded into the existing `combined := forgeconv.Result{}` pipelines without adapter glue. Severity, rule names, and remediation phrasing are preserved verbatim from the old call sites — this package is a re-organization, not a behavior change.

What this package is NOT

The cmd/contractlint binary (and its supporting internal/linter/contract package) performs a fundamentally different check: it runs as a go/analysis multichecker that needs full type information to assert "exported methods on impl types must be declared in the contract interface". That check operates on *analysis.Pass, not on a rootDir walk, and so cannot share this engine. The two are complementary — contractcheck catches structural-shape mistakes at codegen time (before the type checker has anything to chew on); the multichecker catches behavioral-shape mistakes at vet time.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AsResult

func AsResult(fs []forgeconv.Finding) forgeconv.Result

AsResult wraps findings in a forgeconv.Result for callers that want to feed the output through the existing FormatText / combined-result plumbing without changing their shape.

func HasErrors

func HasErrors(fs []forgeconv.Finding) bool

HasErrors reports whether any finding in fs has Severity == SeverityError. Convenience so call sites don't have to roll their own loop.

func Inspect

func Inspect(ctx context.Context, rootDir string, opts Options) ([]forgeconv.Finding, error)

Inspect runs the configured rules over rootDir and returns findings in deterministic (file, line, rule) order. A missing rootDir/internal directory is not an error — it produces an empty result.

The context is honored only at coarse granularity: between rules. Individual rule implementations are tree-walks that complete in milliseconds on real projects, so we don't propagate ctx into the walk loops.

Types

type Options

type Options struct {
	// Rules is the subset of rules to run. nil or empty means
	// "run all rules in [AllRules]".
	Rules []Rule

	// Excludes is the contracts.exclude list from forge.yaml — a list
	// of module-relative slash paths to skip wholesale. Only the
	// internal-package-contract-names rule consults this; the other
	// rules ignore it because the marker convention
	// (`// forge:adapter` / `// forge:interactor`) is itself opt-in.
	Excludes []string
}

Options controls a single Inspect run.

Per-call-site differences between forge's contract-check entry points (pre-codegen check vs. `forge lint --conventions`) all reduce to differences in these fields. Adding a new entry point should never require modifying the engine — it should pick the rule subset and excludes it needs and call Inspect.

type Rule

type Rule string

Rule identifies a contract-shape analyzer by its canonical name. The names match the strings that already appear in finding output, so log-search queries and CI grep patterns survive this refactor unchanged.

const (
	// RuleInternalPackageContractNames enforces the canonical
	// `type Service interface`, `type Deps struct`, and
	// `func New(Deps) Service` trio on every internal-package
	// contract.go. Errors gate the build because the bootstrap codegen
	// template hardcodes these names — a non-canonical contract
	// produces a bootstrap that doesn't compile.
	RuleInternalPackageContractNames Rule = "forgeconv-internal-package-contract-names"

	// RuleInteractorDepsAreInterfaces warns when an interactor-marked
	// package declares a Deps field whose type is a concrete struct
	// (or pointer to one) rather than an interface. The pattern
	// defeats the all-mock-test surface interactors are designed for.
	RuleInteractorDepsAreInterfaces Rule = "forgeconv-interactor-deps-are-interfaces"

	// RuleAdapterNoRPC warns when an adapter-marked package registers
	// a Connect RPC handler. Adapters are outbound-only by convention;
	// inbound RPC means the package should be a service.
	RuleAdapterNoRPC Rule = "forgeconv-adapter-no-rpc"
)

The set of rules this package owns. New rules added here MUST be surfaced in AllRules so the default "run everything" path picks them up.

func AllRules

func AllRules() []Rule

AllRules is the canonical "run everything" list. Callers that pass Options.Rules == nil get this set. It is a getter (not an exported package var) so the returned slice can't be mutated by callers and so it satisfies the exported-vars contract rule (no mutable exported package state).

Jump to

Keyboard shortcuts

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