depsurface

package
v1.0.5 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package depsurface reads what a component KNOWS through its Deps (docs/design/composition-graph.md §2, "least knowledge"): for every Deps field, the type it declares and the part of that type the package actually uses through it. go/types resolves every `s.deps.X` selection to the field it names, however the Deps value was reached, so a method called through the field counts and a same-named method on anything else does not.

Three rules read it (internal/linter/forgeconv/least_knowledge.go):

  • deps-surface: an interface field declares methods the package never needs through it — never calls, and never passes the value on as an interface that declares them;
  • no-container-deps: a field holds a container — the composition root's types, the command tree's Deps, a locator, `any`. (The root config joins the list with K3, when the scaffold gives each component a config block of its own: until then the scaffold itself writes `Config *config.Config`, and a lint that flags forge's own output on every fresh service is noise);
  • deps-no-downcast: a type assertion turns a field back into a concrete type, undoing the capability its interface granted.

and `forge fix narrow-deps` (Narrow) writes the interface the observed calls describe.

Index

Constants

View Source
const (
	DepsFileName   = "deps.go"
	DepsFileMarker = "// forge:deps — consumer-declared interfaces for this package's Deps."
)

DepsFileName is where Narrow declares the interfaces of a package that has no contract.go; DepsFileMarker is its first line, which `forge generate` reads to keep the file's mocks (mock_gen.go) in step with it.

Variables

This section is empty.

Functions

func FindComponentDirs

func FindComponentDirs(projectDir string) ([]string, error)

FindComponentDirs lists the project-relative directories under internal/ whose non-test files declare `type Deps struct`.

func HasDepsMarker

func HasDepsMarker(dir string) bool

HasDepsMarker reports whether dir holds a deps.go Narrow wrote.

func Unused

func Unused(f Field) []string

Unused are the declared methods the package never calls through the field.

Types

type Bloated

type Bloated struct {
	Interface string
	// Methods counts what interfacebloat counts: every method written out and
	// every embedded interface.
	Methods     int
	Limit       int
	LimitSource string
	// Suppressed: narrow-deps wrote a reasoned //nolint:interfacebloat above
	// the type, because the project's lint runs interfacebloat.
	Suppressed bool
}

Bloated is a narrowed interface wider than the project's interfacebloat limit.

func (Bloated) String

func (b Bloated) String() string

type Downcast

type Downcast struct {
	Field  string
	File   string
	Line   int
	Target string
}

Downcast is one type assertion from a Deps field.

type EnvRead

type EnvRead struct {
	File string
	Line int
	// Call is the function read through ("os.Getenv").
	Call string
}

EnvRead is one read of the process environment.

type Field

type Field struct {
	Name string
	File string // project-relative
	Line int
	// Type is the field's type as the consumer would write it.
	Type string
	// Interface is true for an interface-typed field; Methods are its
	// exported methods, sorted.
	Interface bool
	Methods   []string
	// Foreign is true for a named interface declared in another package
	// (the consumer did not declare what it needs).
	Foreign bool
	// Used are the methods the package needs through the field: those it
	// calls, plus every method of each Role.
	Used map[string]bool
	// Roles are the interfaces the field's value is passed on as: a
	// parameter, a struct field (assigned or in a literal) or a conversion
	// whose declared type is an interface with methods. Whatever the
	// receiver does with the value, it does through that interface, so its
	// method set — as the receiver declares it — bounds what the receiver
	// needs, and is in Used.
	Roles []Role
	// Escapes is true when the value also leaves through something that
	// bounds nothing — `any`, a concrete type, a return value, a channel, a
	// map or slice element — so even Used is not the whole story.
	// EscapeAt is where it first does ("file:line").
	Escapes  bool
	EscapeAt string
	// Downcast is true when the package type-asserts the field to a
	// concrete type (Package.Downcasts says where).
	Downcast bool
	// Container names the container the field holds ("" for none).
	Container string
	// RootConfig is true for a field holding the project's root config
	// (pkg/config.Config) rather than the component's own block.
	RootConfig bool
	// contains filtered or unexported fields
}

Field is one Deps field.

type NarrowResult

type NarrowResult struct {
	Dir string
	// File is the project-relative file the interfaces were written to:
	// the package's contract.go, or deps.go.
	File     string
	Narrowed []Narrowed
	Skipped  []Skipped
	// Bloated are the declared interfaces wider than the project's
	// interfacebloat limit.
	Bloated []Bloated
}

NarrowResult is what Narrow did to one package.

func Narrow

func Narrow(projectDir, dir string) (*NarrowResult, error)

Narrow rewrites every foreign interface field of dir's Deps that declares methods the package never needs to an interface the package declares itself, holding exactly the methods it needs (design §2, K1): those it calls, with each interface it passes the value on as (Field.Roles) embedded. The composition still fills the field with the same value: Go satisfies the narrower interface structurally, so compose.go needs no edit, and every call that passes the value on still type-checks.

A field whose value also leaves through something that bounds nothing (Escapes: `any`, a concrete type, a return value) is left alone — what it needs is not all visible here — as is a field the package never uses at all (delete it instead), and an interface the package already declares (edit it in place).

type Narrowed

type Narrowed struct {
	Field, From, To string
	// Methods are every method the new interface holds; Embeds are the
	// interfaces it embeds for the ones the package passes the value on as.
	Methods []string
	Embeds  []string
}

Narrowed is one field Narrow retyped.

type Package

type Package struct {
	// Dir is the package directory, project-relative and slash-separated.
	Dir string
	// Path is the import path.
	Path string
	// DepsFile is the project-relative file declaring Deps.
	DepsFile string
	Fields   []Field
	// Downcasts are type assertions from a Deps field to a concrete type.
	Downcasts []Downcast
	// EnvReads are the package's own reads of the process environment
	// (os.Getenv, os.LookupEnv, os.Environ) in non-test files: configuration
	// the composition never handed it, and a root's config reach cannot see.
	EnvReads []EnvRead
	// contains filtered or unexported fields
}

Package is one component package's Deps, as used.

func Analyze

func Analyze(projectDir string, dirs []string) ([]*Package, error)

Analyze loads the component packages in dirs (project-relative) and reads each one's Deps. A package that does not type-check is skipped: the compiler reports it better than a lint can.

type Role

type Role struct {
	// Type is the interface as the consumer would write it
	// ("threads.Repository"); for an interface literal or a type
	// parameter's constraint, its methods in braces.
	Type string
	// Methods are its exported methods, sorted.
	Methods []string
	// Embeddable is true when the consumer can name the interface (a named
	// interface it may refer to), so a narrowed interface can embed it.
	Embeddable bool
	// contains filtered or unexported fields
}

Role is one interface a Deps field's value is passed on as.

type Skipped

type Skipped struct {
	Field, Type, Reason string
}

Skipped is one interface field Narrow left alone, and why.

Jump to

Keyboard shortcuts

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