Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,32 @@ Set `-include-generated` to report in them anyway,
constable -include-generated ./...
```

## Pure

The pure analyzer checks functions annotated with `//constable:pure`.
A pure function has no observable effect beyond its return values.
It forbids every caller-visible mutation that the nonmutating analyzer forbids, and also forbids package-global writes, `go` spawns, and calls that read the clock, random, environment, or perform I/O.

```go
//constable:pure
func F(p *int) int {
return *p // allowed: read only
}

//constable:pure
func G() {
// reported: reads the clock
_ = time.Now()
}
```

A pure function that reads a global is allowed.
A pure function that writes a global is reported.

The banned calls are `time.Now` and friends, any `math/rand` call, `os` environment and file calls (except error predicates), `fmt` and `log` printing, and the `print` and `println` builtins.
Reads of `os.Args` and `time.Local` are reported too.
Pure functions are deterministic given their arguments, so they are safe to reorder, parallelize, or test without flaky setup.

## Example

This function is reported because it mutates through a pointer parameter,
Expand Down Expand Up @@ -100,6 +126,21 @@ if a && isB() { // allowed

The check also covers `for` conditions and bool assignments such as `c := a && b`, including negated variables, field and map access, and comparisons.

## Lean

The lean analyzer reports unused unexported declarations: functions, types, variables, and constants that nothing in the package references.
Deleting dead declarations keeps the package small, so readers and agents hold less context in mind.

```go
// reported: nothing calls unused
func unused() {}
```

The check stays precise on purpose.
Methods, struct fields, unused parameters, and exported symbols are not reported.
Constants in a group that uses `iota` or hides a value are not reported either, because removing one member would renumber the rest.
Unused parameters may exist to satisfy an interface, and exported symbols may be used outside the package, so package-local evidence cannot prove either one dead.

## Fixes

`-fix` applies the fixes that the analyzers suggest, and `-fix -diff` prints the change instead of writing it.
Expand All @@ -109,8 +150,9 @@ constable -fix ./...
constable -fix -diff ./...
```

Only the methodical analyzer suggests fixes today.
It places every method of a reported type directly after the type declaration, in order, and a method that lives in another file moves into the file that declares its type.
The methodical and lean analyzers suggest fixes.
Methodical places every method of a reported type directly after the type declaration, in order, and a method that lives in another file moves into the file that declares its type.
Lean deletes unused unexported declarations and removes imports that only the deleted declaration used.

A move fixes the import blocks of both files:

Expand Down
4 changes: 4 additions & 0 deletions cmd/constable/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package main
import (
"github.com/nnutter/constable/internal/analysis/complexity"
"github.com/nnutter/constable/internal/analysis/errcheck"
"github.com/nnutter/constable/internal/analysis/lean"
"github.com/nnutter/constable/internal/analysis/methodical"
"github.com/nnutter/constable/internal/analysis/nilaway"
"github.com/nnutter/constable/internal/analysis/nonmutating"
"github.com/nnutter/constable/internal/analysis/predicates"
"github.com/nnutter/constable/internal/analysis/pure"
"github.com/nnutter/constable/internal/analysis/sumtype"
"github.com/nnutter/constable/internal/analysis/testify"
"github.com/nnutter/constable/internal/analysis/testifylint"
Expand All @@ -23,6 +25,8 @@ func main() {
version,
driver.Hooks{AfterLoad: nilaway.UseMainModules},
nonmutating.Analyzer,
pure.Analyzer,
lean.Analyzer,
methodical.Analyzer,
predicates.Analyzer,
testify.Analyzer,
Expand Down
Loading
Loading