Skip to content

fix(linters): reconcile deferred Close error guidance - #67631

Merged
pelikhan merged 3 commits into
mainfrom
pelikhan-deferred-close-linter-consistency
Oct 11, 2026
Merged

pelikhan merged 3 commits into
mainfrom
pelikhan-deferred-close-linter-consistency

Conversation

@pelikhan

Copy link
Copy Markdown
Collaborator

Summary

Fixes #67608.

The issue correctly identifies stale fixture guidance, but the analyzers have complementary AST scopes rather than conflicting enforcement: closeerrorunchecked handles expression statements and assignments, while unchecked_deferredclose handles direct deferred calls. Not reporting an out-of-scope statement is not approval of silently discarding its error.

  • Remove the fixtures' misleading “defer is best practice” guidance and clarify both analyzer descriptions and comments.
  • Make the deferred-close diagnostic recommend a deferred closure that handles the cleanup error.
  • Document the unified policy and an errors.Join example that preserves operation and cleanup errors.
  • Add a combined-analyzer regression fixture covering files, custom closers, interfaces, unchecked closure bodies, propagated errors, no-error methods, analyzer-specific suppressions, and exactly one finding for each unchecked call.

Existing enforcement, registry entries, and CI rollout exclusions are unchanged; neither analyzer is weakened or made to emit duplicate findings.

Validation

  • go test ./pkg/linters/closeerrorunchecked ./pkg/linters/unchecked-deferred-close -count=1 passed.
  • PATH="/opt/homebrew/bin:$PATH" make agent-report-progress passed, including build, changed-file lint, impacted Go tests, schema freshness, and workflow drift checks (337 workflows in sync).
  • make fmt completed Go formatting but its unrelated JavaScript formatting step failed on testdata/copilot_sdk_web_fetch_contract.json with ReferenceError: convertToJson is not defined; incidental JavaScript formatting changes were removed. The final change-scoped gate formatted all changed Go files successfully.

Preserve complementary analyzer scopes and verify shared cleanup patterns without duplicate findings or weaker enforcement.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan marked this pull request as ready for review October 11, 2026 05:34
Copilot AI balanced review requested due to automatic review settings October 11, 2026 05:34
@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Lean already. Ship.

Generated by Ponytail Reviewer for #67631

@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The documentation, diagnostics, analyzer scopes, and regression coverage are consistent with the stated policy.

0 open findings

What changed in this PR

Aligns complementary Close() error analyzers around a unified deferred-cleanup policy.

Changes:

  • Clarifies analyzer scopes and remediation guidance.
  • Documents safe deferred cleanup using errors.Join.
  • Adds combined regression coverage for diagnostics and suppressions.
File Description
pkg/​linters/​unchecked-deferred-close/​unchecked_deferredclose.go Improves scope and diagnostic guidance.
pkg/​linters/​README.md Documents unified close-error policy.
pkg/​linters/​closeerrorunchecked/​testdata/​src/​combined/​combined.go Adds cross-analyzer regression fixtures.
pkg/​linters/​closeerrorunchecked/​testdata/​src/​closeerrorunchecked/​closeerrorunchecked.go Removes misleading defer guidance.
pkg/​linters/​closeerrorunchecked/​closeerrorunchecked.go Clarifies analyzer scope.
pkg/​linters/​closeerrorunchecked/​closeerrorunchecked_test.go Runs both analyzers against shared fixtures.

🧠 Review effort: Balanced


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict

No actionable blocking issues found in the changed lines.

Review notes

I checked the scope split between closeerrorunchecked and unchecked_deferredclose, the new combined regression test, and the updated fixture guidance. The patch keeps analyzer behavior consistent with the documented AST split and adds coverage for the previously misleading defer examples without weakening either analyzer or introducing obvious duplicate-report paths.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 14.2 AIC · ⌖ 5.34 AIC · ⊞ 21.2K
Comment /review to run again

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

✅ Test Quality Score: 100/100 — Excellent

Analyzed 1 test: 1 design, 0 implementation, 0 violation(s).

📊 Metrics (1 test)
Metric Value
Analyzed 1 (Go: 1, JS: 0)
✅ Design 1 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 1 (100%)
Duplicate clusters 0
Inflation No
🚨 Violations 0
Test File Classification Issues
TestComplementaryCloseErrorAnalyzers pkg/linters/closeerrorunchecked/closeerrorunchecked_test.go:21 Design test None

Verdict

✅ Passed. 0% implementation tests (threshold: 30%). Strong regression test for combined analyzer behavior.

🧪 Test quality analysis by Test Quality Sentinel · copilot · haiku45 · 16.8 AIC · ⌖ 6.77 AIC · ⊞ 8.2K · ◷
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 100/100. 0% implementation tests (threshold: 30%).

🧪 Test quality analysis by Test Quality Sentinel · copilot · haiku45 · 16.8 AIC · ⌖ 6.77 AIC · ⊞ 8.2K
Comment /review to run again

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (146 new lines in pkg/) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/67631-complementary-ast-scopes-for-close-error-analyzers.md — review and complete it before merging.

🔒 This PR cannot merge until an ADR is linked in the PR body.

🔎 Evidence used
  • No implementation label; enforcement triggered by volume: 146 additions in business-logic directories (threshold 100), 6 files changed.
  • No ADR link or Context/Decision/Alternatives/Consequences section in the PR body; no docs/adr/67631-*.md on the branch (latest existing ADRs are 67424, 67425, 67509, 67519).
  • Decision inferred from the diff: the two close-error analyzers are reconciled as complementary AST scopes (ExprStmt/assignments vs. DeferStmt) under one documented policy, rather than merged or relaxed — analyzer descriptions, fixtures, pkg/linters/README.md policy section, and a combined-analyzer regression fixture in closeerrorunchecked/testdata/src/combined.
📋 What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff.
  2. Complete the missing sections — confirm the rejected alternatives (merging both analyzers; relaxing unchecked_deferredclose) match your actual reasoning, and note whether retiring the CI rollout exclusions is a planned follow-up.
  3. Set the status from Draft to Proposed/Accepted once reviewed.
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-67631: Treat the Two Close-Error Analyzers as Complementary AST Scopes Under One Policy

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

📋 Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · pi · opus50 · 44.2 AIC · ⌖ 54 AIC · ⊞ 1.8K · ◷
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd (via pr-triage: bug_fix). The root cause (stale, misleading fixture comments implying bare defer Close() was approved) is fixed consistently across both analyzer docs, the README, and renamed fixture functions — confirmed by running the test suite locally.

📋 Key Themes & Highlights

Verification performed

  • go test ./pkg/linters/closeerrorunchecked/... ./pkg/linters/unchecked-deferred-close/... — all pass.
  • go vet on both packages — clean.
  • Doc-sync tests (TestDocGo_CountMatchesBullets, TestDocGo_AnalyzersMatchREADME, TestDocSurfacesMatchRegistryAndSpecList) still pass after the README edit.
  • Confirmed by probe test that the new combined.go fixture genuinely requires both analyzers running together (single-analyzer run fails with unmatched diagnostics), so the regression coverage is real, not incidental.

One actionable suggestion

  • The new TestComplementaryCloseErrorAnalyzers hand-rolls a mini multi-analyzer runner (shallow-copies *pass, swaps Analyzer/Report per iteration) directly in the test file. This works today, but the technique is non-obvious and likely to be needed again for other complementary-analyzer pairs hinted at by the new README policy section. Extracting it into a small analyzerutil helper would make it reusable and reduce the risk of someone copy-pasting the *pass plumbing incorrectly. See inline comment.

Positive highlights

  • ✅ Root cause addressed at the source (fixture comments + analyzer Doc strings + package comments), not just symptom-patched.
  • ✅ combined.go fixture is comprehensive: covers files, custom closers, interfaces, closures, suppressions, and the no-error-method case — good boundary coverage per /tdd.
  • ✅ Clear, consistent cross-references between the two analyzers' descriptions prevent the original confusion from recurring.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 123.9 AIC · ⌖ 13.6 AIC · ⊞ 10.3K
Comment /matt to run again

Comment thread pkg/linters/closeerrorunchecked/closeerrorunchecked_test.go
Extract diagnostic prefixing and isolated dependency results into a tested regression helper with explicit unsupported-configuration errors.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan merged commit 1064352 into main Oct 11, 2026
41 checks passed
@pelikhan
pelikhan deleted the pelikhan-deferred-close-linter-consistency branch October 11, 2026 07:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unchecked_deferredclose (79th linter) directly contradicts closeerrorunchecked's documented best-practice for defer Close()

2 participants