Skip to content

[duplicate-code] Duplicate Code: type-assertion linter run/visit scaffold duplicated across analyzers #69118

Open

Description

@github-actions

🔍 Duplicate Code Detected: go/analysis Type-Assertion Linter Scaffold

Analysis of commit 5bf0b44 (github/gh-aw)

Assignee: @copilot

Summary

The type-assertion linters in pkg/linters/ duplicate a near-identical run scaffold and the opening of their visitor: analyzerutil.Indexes(pass) -> build a per-file parent map with astutil.BuildParentMap -> nodeFilter := []ast.Node{(*ast.TypeAssertExpr)(nil)} -> analyzerutil.Preorder(...), followed by the same typeAssert, ok := n.(*ast.TypeAssertExpr) / if typeAssert.Type == nil { return } / ShouldSkipFilename guard block.

Duplication Details

Pattern: parent-map + Preorder + type-assert guard

  • Severity: Medium
  • Occurrences: 3 analyzers share the full parent-map form (~45 duplicated lines); the broader analyzerutil.Indexes(pass) run scaffold appears in 78 linter files
  • Locations:
    • pkg/linters/typeassertionnil/typeassertionnil.go (run: 21-39; guard: 44-60)
    • pkg/linters/uncheckedtypeassertion/uncheckedtypeassertion.go (run: 23-41; guard: 46-62)
    • pkg/linters/typeassertionokdiscarded/typeassertionokdiscarded.go (run: 20-37; guard: 42-56)
  • Code Sample:
    func run(pass *analysis.Pass) (any, error) {
        noLintIndex, generatedFiles, err := analyzerutil.Indexes(pass)
        if err != nil { return nil, err }
        fileParents := make(map[*ast.File]map[ast.Node]ast.Node)
        for _, f := range pass.Files { fileParents[f] = astutil.BuildParentMap(f) }
        nodeFilter := []ast.Node{(*ast.TypeAssertExpr)(nil)}
        return analyzerutil.Preorder(pass, nodeFilter, func(n ast.Node) { ... })
    }

Impact Analysis

  • Maintainability: Changes to parent-map construction or skip logic must be mirrored across the type-assertion analyzers.
  • Bug Risk: Divergence already present (one builds parent map eagerly, another looks it up lazily via astutil.FileForPos).
  • Code Bloat: Repeated scaffold across analyzers that analyzerutil was created to reduce.

Refactoring Recommendations

  1. Add a parent-map-aware runner to pkg/linters/internal/analyzerutil, e.g. RunTypeAssertVisitor(pass, func(pass, ta *ast.TypeAssertExpr, parents map[ast.Node]ast.Node, skip ...) ) that performs Indexes + parent-map build + Preorder + the common Type == nil / ShouldSkipFilename / nolint guards.
    • Estimated effort: medium (3-5h)
    • Benefits: each type-assertion linter keeps only its unique predicate/diagnostic; consistent skip behavior.
  2. Consider a broader analyzerutil.Run(pass, nodeFilter, visit) helper to collapse the 78-file Indexes scaffold where a parent map is not needed.

Implementation Checklist

  • Review duplication findings
  • Add shared runner in analyzerutil
  • Migrate typeassertionnil / uncheckedtypeassertion / typeassertionokdiscarded
  • Verify linter testdata still passes
Analysis Metadata
  • Analyzed Files: pkg/linters//.go (excluding testdata)
  • Detection Method: Serena activation + normalized block hashing + grep cross-reference
  • Commit: 5bf0b44

Generated by 🔍 Duplicate Code Detector · pi · opus48 · 139.4 AIC · ⌖ 42.8 AIC · ⊞ 1.6K · ◷

  • expires on Oct 17, 2026, 1:56 PM UTC-08:00

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions