cleanorder

command module
v0.0.0-...-4259a12 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2025 License: MIT Imports: 13 Imported by: 0

README

cleanorder

Reorder go source code files in clean coding order (caller-before-callee).

Use -dry to print the new version to STDOUT. Without -dry, it edits files in place.

Ordering rules implemented

This tool reorders declarations in a single Go source file to follow a caller-before-callee, readability-driven layout while making minimal textual movement and preserving original grouping/spacing where possible. Below is a succinct summary of the rules and classifications the program applies when producing output.

High-level emission order

  • Package header, imports and file-level comments are preserved at the top.

  • All const/var declaration blocks are emitted next, in original order, separated by blank lines, except when a declaration explicitly references a type declared in the same file (for example, typed iota constants like AttackRolled Kind = iota or var x SomeType). Such declarations are treated as users of that type and are emitted with that type’s users instead of in the global const/var section. This enforces the precedence that types come before their users.

    • Additionally, when a var initialiser references constants declared in the same file (e.g., var DirectionsURDL = [...]Dir{Up, Right, Down, Left}), the constant block that defines those identifiers is emitted before that variable, even if the original source placed the var above the const. This keeps users of constants beneath the constants they use.
  • "Independent" free functions (non-methods that have no calls to or from other functions in the file and are not constructors named New*) are emitted next in their original order.

  • For each named type declared in the file (in source order):

    • Emit the type declaration (once).
    • Emit constructor functions for that type (free functions named NewX that return X or *X). Constructors keep their original order.
    • Emit the type's methods and their helper functions as a clustered group. Within that cluster, helpers are placed directly after the first method that calls them (callee-after-first-caller). If a helper is called by multiple methods, it is still anchored under the chosen first caller; the later callers do not force the helper to move further down. Note: A free function that is used as a method helper is not treated as a generic "user" of any types it happens to reference. This prevents helpers (e.g., readBody/handleError used by ServeHTTP) from being emitted before their first caller in another type’s user section.
    • Emit functions and methods that "use" the type (any function or method that references the type in a signature/body or calls the type's constructors/methods). If the type has no constructors or methods, its users are still emitted immediately after the type declaration.
      • Additionally, const/var declarations that reference the type (e.g., typed iota const (...) SomeType = iota) are emitted here with the type’s users, not in the early const/var section.
      • Within the type’s users section, if a free function user calls private helper functions, those helpers are packed immediately beneath that user in the order they are first called (first-use order). This mirrors the helper packing used for method clusters: a helper anchored under its first caller is not forced further down by later callers. This ensures examples like Generate() are followed by chooseRoom, roomsIntersecting, carveRoom, connectRooms in that order, and that nested helpers (e.g., carveHorizontal/Vertical calling carveLine) are packed depth-first under the first caller (carveHorizontal, carveLine, carveVertical).
  • Any remaining type blocks that weren't emitted in the typed loop are written next.

  • Remaining free functions are emitted by connected components of the call graph. Each component is ordered so callers appear before callees and local call order is preserved; components and intra-component items otherwise retain original ordering as a tie-breaker.

Classification rules used by the emitter

  • Constructors: free functions named New... that have results mentioning a declared type in this file (allows pointer returns *T).
  • Methods: functions with a receiver whose base type matches a declared type in this file.
  • Helpers: free functions that are called by methods of a given type. These are grouped with the type's methods so related helpers appear near the methods that reference them.
  • Users: functions and methods that reference a type (in params/results/struct fields or body) or that call the type's constructors/methods. Users are emitted after the type's methods/constructors cluster, or directly after the type when there are no methods/constructors.
    • Incidental types: when a type has no constructors or methods and its only users are methods whose receiver types are not declared in the same file, the type declaration is considered incidental and is emitted immediately before the first such user instead of anchoring its own section. This prevents incidental return/parameter types from disrupting grouping and the callee-after-first-caller ordering within the receiver's method cluster.
  • Independent free functions: non-methods that are not constructors and have no incoming or outgoing call edges within the file.

Ordering constraints and algorithms

  • Callee-after-first-caller (within clusters): for helpers grouped under a type's methods, each helper is anchored immediately beneath a single designated first caller. When a helper has multiple callers among the methods, the first caller is chosen as the method that calls the most helpers (ties broken by earliest appearance). Other callers do not impose additional predecessor constraints on that helper inside the cluster. Outside of clusters (e.g., among free functions), callers still precede their callees as before.
    • The same "pack helpers beneath the first caller" idea is applied within a type’s users section for free-function users (non-methods). After emitting a user function, its private helpers are placed directly below it in the order of first use (callee-after-first-caller). This may place a shared helper above later callers of that helper; that’s acceptable and preferred for readability. When truly impossible due to other constraints, the tool falls back to placing the helper after the blocking declarations.
    • Precedence: the callee-after-first-caller rule for a type's method cluster takes precedence over incidental standalone type declarations that happen to be referenced or returned by those methods. Such incidental types are inlined immediately above the first using method and do not form their own user section.
    • Type-before-users precedence (raised): a type’s declaration must appear before any declarations that use it, including methods on other receiver types and other type declarations that reference it in their own definitions (for example, a struct field typed as []AttackDetail). When necessary, this can cause types to be emitted earlier than their original source order so that the type precedes methods that return or reference it (e.g., a Summary type will be emitted before an Events.Summary() method that returns it). Const/var declarations that reference the type are likewise treated as users and emitted after the type rather than in the global const/var section.
  • Call sequencing: if a function calls A then B in that order, the tool records that sequence and prefers to keep A before B when both are in the reordering subset.
  • Minimal movement: reorders try to move as little as possible from the original layout (stable sort by original offsets is used as a base).
  • Packing: within clusters, helpers are packed contiguously under their first caller in the order the caller first uses them. Among remaining reordering regions, packing behaves as before while respecting constraints.

Tie-breakers

  • When ordering is otherwise unconstrained, exported (public) functions and methods are preferred over unexported (private). Within each group, original file order is preserved. This applies across independent functions, call-graph components, and type users.

  • Deterministic across runs: any remaining unordered sets (for example, disconnected call-graph components, incidental types injected immediately before a user, or types injected to satisfy typed const/var dependencies) are processed in a stable, deterministic order (exported-first, then original source position). This avoids spurious diffs where repeated runs would otherwise produce different but equally valid orders.

Limitations (brief)

  • Analysis is file-local: it recognizes calls by identifier/selector name but does not fully resolve cross-package symbols or handle shadowing across multiple files in a package. Selector-based calls use only the selector name (package qualification is ignored).
  • The tool is conservative about detection of constructors and "uses" of a type (it looks in signatures and the function body for identifier matches).

These rules are intentionally conservative to avoid breaking code while improving readability by grouping related declarations and ensuring callers come before the functions they call.

Comment handling

  • Doc comments (// or /* */) that are attached to a declaration are emitted with that declaration. The tool ensures the file header (package/imports) is split from the first declaration at the declaration's doc start to avoid duplicating the doc text when reordering.
  • Inline trailing comments that appear on the same line as a declaration or a function's closing brace (for example, //nolint) are preserved on that same line in the output.

Documentation

The Go Gopher

There is no documentation for this package.

Jump to

Keyboard shortcuts

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