Skip to content

uncheckedtypeassertion: recognize safe comma-ok form in var init and parenthesized assertions#39774

Merged
pelikhan merged 6 commits into
mainfrom
copilot/fix-unsafe-type-assertion
Jun 17, 2026
Merged

uncheckedtypeassertion: recognize safe comma-ok form in var init and parenthesized assertions#39774
pelikhan merged 6 commits into
mainfrom
copilot/fix-unsafe-type-assertion

Conversation

Copilot AI commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

uncheckedtypeassertion only treated x.(T) as safe when its direct parent was an AssignStmt, which produced false positives for valid comma-ok initializations and parenthesized assertions. This PR broadens safe-form detection to match Go’s assignment/initialization semantics without weakening existing unsafe checks.

  • Safe-form parent resolution

    • Unwraps *ast.ParenExpr ancestors before evaluating assertion context.
    • Detects comma-ok safety across both assignment and initialization nodes.
  • Expanded safe contexts

    • Keeps existing safe handling for:
      • v, ok := x.(T)
      • v, ok = x.(T)
    • Adds safe handling for:
      • var v, ok = x.(T)
      • v, ok := (x.(T))
  • Preserved unsafe reporting

    • Single-value assertions in initialization/expression contexts remain reportable (e.g., var v = x.(T), _ = x.(T), f(x.(T))).
  • Analyzer fixture updates

    • Adds negative fixtures for var v, ok = ... and parenthesized comma-ok assignment.
    • Adds/keeps positive fixture coverage for single-value var initialization.
parent := parents[typeAssert]
for paren, ok := parent.(*ast.ParenExpr); ok; paren, ok = parent.(*ast.ParenExpr) {
	parent = parents[paren]
}

switch p := parent.(type) {
case *ast.AssignStmt:
	return len(p.Lhs) == 2 && len(p.Rhs) == 1
case *ast.ValueSpec:
	return len(p.Names) == 2 && len(p.Values) == 1
}

Copilot AI and others added 2 commits June 17, 2026 12:31
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix unchecked type assertion detection for initialization uncheckedtypeassertion: recognize safe comma-ok form in var init and parenthesized assertions Jun 17, 2026
Copilot AI requested a review from pelikhan June 17, 2026 12:41
@pelikhan pelikhan marked this pull request as ready for review June 17, 2026 13:26
Copilot AI review requested due to automatic review settings June 17, 2026 13:26

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.

Pull request overview

This PR updates the uncheckedtypeassertion Go analyzer to recognize additional safe comma-ok type assertion contexts by unwrapping parenthesized assertions and treating var v, ok = x.(T) as safe, reducing false positives without relaxing unsafe single-value assertion reporting.

Changes:

  • Expand “safe two-value assertion” detection to unwrap *ast.ParenExpr parents and accept *ast.ValueSpec (var v, ok = ...) in addition to *ast.AssignStmt.
  • Update analyzer fixtures to cover safe var v, ok = ... and parenthesized comma-ok assignment, and keep coverage for unsafe single-value var v = ....
  • Includes workflow .lock.yml schema changes for pull_request_number validation across multiple generated workflows.
Show a summary per file
File Description
pkg/linters/uncheckedtypeassertion/uncheckedtypeassertion.go Broadens safe-context detection for comma-ok assertions (paren unwrapping + ValueSpec).
pkg/linters/uncheckedtypeassertion/testdata/src/uncheckedtypeassertion/uncheckedtypeassertion.go Adds/adjusts fixtures for new safe and still-unsafe assertion forms.
.github/workflows/test-quality-sentinel.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/smoke-copilot.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/smoke-copilot-arm.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/smoke-copilot-aoai-entra.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/smoke-copilot-aoai-apikey.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/smoke-claude.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/security-review.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/refiner.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/pr-triage-agent.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/pr-nitpick-reviewer.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/pr-code-quality-reviewer.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/mattpocock-skills-reviewer.lock.yml Generated lockfile update affecting pull_request_number validation.
.github/workflows/grumpy-reviewer.lock.yml Generated lockfile update affecting pull_request_number validation.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 15/15 changed files
  • Comments generated: 13

},
"pull_request_number": {
"optionalPositiveInteger": true
"issueOrPRNumber": true
},
"pull_request_number": {
"optionalPositiveInteger": true
"issueOrPRNumber": true
},
"pull_request_number": {
"optionalPositiveInteger": true
"issueOrPRNumber": true
},
"pull_request_number": {
"optionalPositiveInteger": true
"issueOrPRNumber": true
},
"pull_request_number": {
"optionalPositiveInteger": true
"issueOrPRNumber": true
},
"pull_request_number": {
"optionalPositiveInteger": true
"issueOrPRNumber": true
},
"pull_request_number": {
"optionalPositiveInteger": true
"issueOrPRNumber": true
},
"pull_request_number": {
"optionalPositiveInteger": true
"issueOrPRNumber": true
},
"pull_request_number": {
"optionalPositiveInteger": true
"issueOrPRNumber": true
},
"pull_request_number": {
"optionalPositiveInteger": true
"issueOrPRNumber": true
@github-actions github-actions Bot mentioned this pull request Jun 17, 2026
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

No _test.go files were added or modified in this PR. Test Quality Sentinel skipped. Note: the PR does add three new annotated scenarios to the analysistest testdata file (GoodTwoValueVarDecl, GoodTwoValueParen, BadSingleValueVarDecl), which extend the behavioral coverage of the existing TestAnalyzer test — but testdata files are not scored by this sentinel.

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed for PR #39774: it does not have the 'implementation' label (has_implementation_label=false) and has only 42 new lines of code in business logic directories (≤100 threshold, no custom .design-gate.yml config).

@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 /tdd and /diagnose — approving with two minor test-coverage suggestions.

📋 Key Themes & Highlights

Positive Highlights

  • Root cause correctly identified: the old parents[typeAssert].(*ast.AssignStmt) lookup missed both *ast.ParenExpr wrappers and *ast.ValueSpec parents.
  • ParenExpr unwrap loop is clean: iterates until a non-paren ancestor is found; handles the nil-parent edge case by falling through to default: false — correct and safe.
  • ValueSpec check mirrors the AssignStmt check: len(p.Names) == 2 && len(p.Values) == 1 precisely matches the comma-ok grammar for var declarations.
  • No weakening of unsafe reporting: single-value var declarations (var s = x.(T)) correctly remain flagged, confirmed by BadSingleValueVarDecl.
  • Lock-file changes are auto-generated (schema rename optionalPositiveInteger → issueOrPRNumber): no manual review needed.

Minor Test Gaps (non-blocking)

Two combinations of the new code paths aren't exercised by any fixture:

  1. Parenthesized var-declaration: var s, ok = (v.(string)) — exercises the loop and the ValueSpec branch together (see inline comment at line 52).
  2. Double-parens: s, ok := ((v.(string))) — confirms the loop iterates more than once (see inline comment at line 48).

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

if ok {
fmt.Println(s)
}
}

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.

[/tdd] Missing combined test: var s, ok = (v.(string)) — parenthesized var-declaration is not exercised, even though it threads through both new code paths (ParenExpr unwrapping and the ValueSpec case). If a future refactor accidentally broke the interaction, no test would catch it.

💡 Suggested fixture
// Good: parenthesized two-value var declaration is safe.
func GoodTwoValueParenVarDecl(v interface{}) {
	var s, ok = (v.(string))
	if ok {
		fmt.Println(s)
	}
}

This is the intersection of GoodTwoValueVarDecl and GoodTwoValueParen and is the only path that exercises the ParenExpr-unwrap loop together with the *ast.ValueSpec branch.


// Good: parenthesized two-value assignment is safe.
func GoodTwoValueParen(v interface{}) {
s, ok := (v.(string))

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.

[/tdd] Consider adding a multi-level parens test: s, ok := ((v.(string))). The loop at isSafeTwoValueAssertion is designed to handle any depth of nesting, but no fixture confirms this works for more than one level.

💡 Suggested fixture
// Good: double-parenthesized two-value assignment is safe.
func GoodTwoValueDoubleParen(v interface{}) {
	s, ok := ((v.(string)))
	if ok {
		fmt.Println(s)
	}
}

Without this, if the loop were accidentally changed to only strip a single ParenExpr, double-paren cases would silently become false positives 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.

Non-blocking: logic is correct, two test coverage gaps

The core change is sound: paren-unwrapping and ValueSpec detection are both correctly implemented and preserve all pre-existing unsafe-detection semantics.

Gaps found (non-blocking)

Medium — paren + ValueSpec combo untested (see inline comment at GoodTwoValueVarDecl)

The paren-unwrapping loop is exercised for AssignStmt (via GoodTwoValueParen), but not for ValueSpec. The pattern var s, ok = (v.(string)) should be added as a fixture.

Low — regular re-assignment with parens untested (see inline comment at GoodTwoValueParen)

v, ok = (x.(T)) (using = instead of :=) is structurally identical in the AST but is not exercised by any fixture.

Note: existing comments on the 13 .lock.yml changes already flag those as unrelated; no additional comment added here.

🔎 Code quality review by PR Code Quality Reviewer

if ok {
fmt.Println(s)
}
}

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.

Paren + ValueSpec combination is not tested, so the paren-unwrapping path for the new ValueSpec branch has zero coverage.

💡 Suggested addition

GoodTwoValueVarDecl tests var s, ok = v.(string) (ValueSpec branch) and GoodTwoValueParen tests s, ok := (v.(string)) (paren→AssignStmt). Neither tests the cross-product: var s, ok = (v.(string)), which goes through the paren-unwrapping loop and then lands on ValueSpec.

If the loop were ever changed to stop early (e.g. breaking on nil before reaching the real parent), this combination would silently produce a false positive and no existing test would catch it.

Add:

// Good: parenthesized two-value var declaration is safe.
func GoodTwoValueVarDeclParen(v interface{}) {
	var s, ok = (v.(string))
	if ok {
		fmt.Println(s)
	}
}


// Good: parenthesized two-value assignment is safe.
func GoodTwoValueParen(v interface{}) {
s, ok := (v.(string))

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.

v, ok = (x.(T)) (paren + regular re-assignment) is not covered. The only paren fixture uses := (short var decl), leaving the equivalent re-assignment path untested.

💡 Suggested addition

Both := and = produce an AssignStmt, so both are handled by the same case *ast.AssignStmt branch. But a regression that accidentally checked assign.Tok == token.DEFINE would only be caught if the = form is also exercised.

Add:

// Good: parenthesized two-value re-assignment is safe.
func GoodTwoValueParenAssign(v interface{}) {
	var s string
	var ok bool
	s, ok = (v.(string))
	if ok {
		fmt.Println(s)
	}
}

Copilot AI and others added 3 commits June 17, 2026 13:53
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Completed a pr-finisher pass.

Addressed the scope issue by removing unrelated workflow lockfile churn and added the missing uncheckedtypeassertion fixture coverage requested by bot review. Latest commit: 5709918b30.

@pelikhan pelikhan merged commit 503db73 into main Jun 17, 2026
29 checks passed
@pelikhan pelikhan deleted the copilot/fix-unsafe-type-assertion branch June 17, 2026 14: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.

3 participants