Skip to content

Add error-first checker: enforce error assertion before result assertions - #313

Open
mmorel-35 wants to merge 2 commits into
Antonboom:masterfrom
mmorel-35:error-first
Open

Add error-first checker: enforce error assertion before result assertions#313
mmorel-35 wants to merge 2 commits into
Antonboom:masterfrom
mmorel-35:error-first

Conversation

@mmorel-35

@mmorel-35 mmorel-35 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Adds a new error-first AdvancedChecker that flags testify assertions on result variables when the associated error return has not been asserted first — or was discarded with _.

Behaviour

// ❌ flagged
res, err := myfunc()
assert.NotNil(t, res) // error-first: assert error before making other assertions

res, _ := myfunc()
assert.NotNil(t, res) // error-first: error return value was discarded; assert the error before asserting the result

res, err := myfunc()
assert.Equal(t, 0, res, err) // error-first: assert error before making other assertions (err is only message arg)

// ✅ valid
res, err := myfunc()
require.NoError(t, err)
assert.NotNil(t, res)

res, err := myfunc()
assert.Nil(t, err) // assertion argument counts
assert.NotNil(t, res)

res, err := myfunc()
if assert.NoError(t, err) { // if-condition counts as checked
    assert.NotNil(t, res)
}

A testify assertion satisfies the requirement when it references the error variable in assertion-value arguments (not only in message arguments like msgAndArgs or *f format/message params).

Key design decisions

  • Error in any return position is tracked, not just last
  • Any testify assertion on the error variable in assertion-value arguments satisfies the requirement — not just dedicated error-checking functions
  • One error assertion satisfies all non-error LHS variables from the same call
  • An error assertion in an if condition counts for the entire if body
  • Reassignment resets the requirement — res, err = f2() requires a new error assertion
  • Reassignment from non-multi-return expressions clears tracking to avoid stale false positives
  • Transitive use (res2 := process(res)) is not flagged — process has no error return
  • No autofix (requires human judgment on which assertion to use)
  • Enabled by default

Changes

  • internal/checkers/error_first.go — new AdvancedChecker; two-pass: collect result→assignInfo maps, then flag non-error testify calls whose args include a tracked result variable without a prior error assertion; error-check detection now ignores message-only usage of err
  • internal/checkers/checkers_registry.go — register checker
  • internal/testgen/gen_error_first.go + main.go — test fixture generator covering invalid/valid cases, including message-arg and reassignment scenarios
  • README.md — table row + documentation section

Comment thread analyzer/testdata/src/checkers-default/error-first/error_first_test.go Outdated
Comment thread internal/checkers/checkers_registry.go Outdated
…rtions

Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
…first by default

Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llm-based LLM shit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants