internal

package
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package internal implements the declscope analysis.

The analysis answers two different questions about two kinds of declaration.

Package-level identifiers compete in one flat namespace, so the problem there is namespace pollution. An unexported identifier is private to its file's namespace unless a directive widens it, and the naming rule asks it to carry that namespace somewhere in its name, so that the owning unit is legible at every use site. The namespace marks ownership. It never grants reach.

Methods and struct fields are already namespaced by the type that owns them and cannot collide with anything, so the problem there is not pollution but encapsulation: Go makes every unexported member visible to the whole package, with no way to say otherwise. Members are exempt from the naming rule, and their bound is the namespace of the type, not of the file.

surplus.go implements the surplus rule: a //declscope:package directive is reported when declscope can see no use of what it widens from another namespace. spec/surplus.fsl is the model; NeverReportsReachable is the property everything here serves.

The rule is unlike the others in that it concludes from an absence. boundary needs a use to find and qualify reads a name, but this rule reports because it saw nothing — so every way a declaration can be reached without its name being spelled is a way for the rule to be wrong, and being wrong is expensive: the advice is to delete a directive, and deleting one that was holding something up breaks a package the analysis never saw. Every check below therefore silences the rule on a doubt, and there is no fix.

Reach the reference index cannot see, and what answers for it:

  • another namespace spells the name -> c.refs (usedOutside)
  • a struct conversion writes fields pairwise, spelling none of them
  • a method set satisfies an interface contract in the package (satisfies)
  • an exported type carries an unexported method out of the package, where an importer completes the satisfaction (carrierExposed)
  • //go:linkname or //export names the declaration as text (linkname)
  • generated files, cgo and assembly hold reference sites the analysis never reads (opaqueSource) — the rule switches off for the package

seesAllFiles is separate: with -test=false, in the non-test variant of a package with in-package tests, or with build-excluded files, some file that may hold the use was never read, and the rule switches off rather than guess.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Collect

func Collect(pass *analysis.Pass, opts Options) []baseline.Key

Collect returns every violation in the package, ignoring any configured baseline. It is the entry point used to regenerate a baseline.

func Run

func Run(pass *analysis.Pass, opts Options) (any, error)

Run performs the analysis for one package.

Types

type Mode

type Mode int

Mode says when a name must carry its namespace: always, never, or only once a package has a second namespace. Only rules.qualify reads one.

const (
	// ModeNever disables the rule.
	ModeNever Mode = iota

	// ModeAlways applies the rule to every package. For the naming rule this
	// means a package gaining its second namespace is not a mass rename.
	ModeAlways

	// ModeOnDemand applies the rule only to a package with more than one
	// namespace. In a package with one there is no boundary for a prefix to
	// mark: every other rule is structurally inert there, since every
	// reference is already inside the single namespace, and a prefix repeated
	// on every declaration would distinguish nothing.
	ModeOnDemand
)

func (Mode) Applies

func (m Mode) Applies(namespaces int) bool

Applies reports whether the rule applies to a package with the given number of namespaces.

func (Mode) String

func (m Mode) String() string

String returns the spelling the settings use.

type ModeSet

type ModeSet []Mode

ModeSet is the values one setting accepts, in the order an error message names them. Each setting declares its own, so that a rejected value is answered with what that setting accepts rather than with everything Mode can hold.

func (ModeSet) Parse

func (s ModeSet) Parse(value string) (Mode, bool)

Parse reads a setting's value: always, never or ondemand, and of those only the members of the set.

func (ModeSet) String

func (s ModeSet) String() string

String lists the accepted spellings the way an error message names them: "always, never or ondemand".

type Options

type Options struct {
	// Unexported is the scope of a declaration that states none of its own and
	// inherits none. There is no Exported counterpart: what is reachable from
	// outside the package is not declscope's subject, and a key that claimed
	// otherwise would promise an enforcement the analysis cannot perform.
	Unexported scope.Scope

	// Qualify says when a package-level declaration must carry its namespace
	// somewhere in its name.
	Qualify Mode

	// Vocabulary lists, per namespace, extra words that carry the namespace as
	// its own spelling would: irregular inflections and domain synonyms that
	// no generated form reaches (mouse: wheel, index: indices). A word is
	// matched exactly the way the namespace is — starting at a word boundary,
	// with the right edge free — so it is a spelling, never a scope.
	Vocabulary map[string][]string

	// AllowSurplus turns the surplus rule off. The rule reports a
	// //declscope:package directive when no use from another namespace is
	// visible to declscope, and is on by default: a directive nobody needed is
	// a thing the author would want told.
	//
	// The polarity is stated rather than inverted in the reader's head. A key
	// named surplus would have read as "surplus: yes please", which is the
	// opposite of what setting it to true would do.
	//
	// The rule never has a fix. It concludes from an absence, so every case it
	// cannot see is one where the directive stays and the advice would be to
	// delete it.
	AllowSurplus bool

	// NameExported widens the naming rule to exported declarations. Inside
	// the package an exported name is read as bare as any other, so the package
	// qualifier that explains an external use is absent exactly where the
	// namespace mark is wanted. The violation is reported. The rename is never
	// offered, since the uses outside the package cannot be seen.
	NameExported bool

	// Exclude holds path globs, spelled the way the config file spells them.
	// A pattern that names a path — anything holding a separator other than a
	// leading ** — is anchored to ExcludeBase, so it speaks about the tree
	// under the config file that states it and nothing else. A bare file name
	// and a leading **/ float, matching at any depth, which is what both
	// spellings mean in a .gitignore.
	Exclude []string

	// ExcludeBase is the directory the anchored patterns are relative to: the
	// directory of the config file that states them. Empty leaves every
	// pattern floating, which is all that can be done when no config file said
	// where "here" is.
	ExcludeBase string

	// BaselinePath is the baseline file that applies, resolved relative to
	// the config file that named it or found by the default-named lookup.
	// Empty means no baseline.
	BaselinePath string

	// Baseline suppresses violations that were already present when declscope
	// was adopted. It is nil when none is configured, and also while a
	// baseline is being regenerated: config.Resolve loads it, config.
	// ResolveForBaseline deliberately does not, so that a baseline which fails
	// to parse cannot block its own regeneration.
	Baseline *baseline.Set
	// contains filtered or unexported fields
}

func DefaultOptions

func DefaultOptions() Options

DefaultOptions mirrors the rules stated in the README: every declaration in the subject is private to its namespace until something widens it. The naming rule is off by default — measured over the repositories this tool was built against, packages with zero boundary violations still drew dozens of naming ones, and whether a name reads well with its namespace in it depends on the part of speech of the file name, which the tool cannot see. A codebase that wants the convention states rules.naming.qualify itself.

func (*Options) Compile

func (o *Options) Compile() error

Compile prepares the exclude patterns. It must be called before use.

It does not load the baseline. Loading is the resolver's decision, since the same options serve both analysis, where the baseline suppresses, and regeneration, where the existing file must be ignored — otherwise one that fails to parse could never be regenerated.

func (Options) Excluded

func (o Options) Excluded(path string) bool

Excluded reports whether a file is outside the scope of the analysis.

Directories

Path Synopsis
Package baseline records violations that already exist in a codebase so that adopting declscope does not require fixing them all at once.
Package baseline records violations that already exist in a codebase so that adopting declscope does not require fixing them all at once.
Package config loads declscope's YAML configuration.
Package config loads declscope's YAML configuration.
Package directive parses declscope comment directives.
Package directive parses declscope comment directives.
Package namespace resolves the pseudo-namespace that a Go source file belongs to.
Package namespace resolves the pseudo-namespace that a Go source file belongs to.
Package rule names declscope's checks.
Package rule names declscope's checks.
Package scope defines the two pseudo visibility levels that declscope layers on top of Go's exported/unexported distinction.
Package scope defines the two pseudo visibility levels that declscope layers on top of Go's exported/unexported distinction.

Jump to

Keyboard shortcuts

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