Skip to content

Refactor: extract generateModules helper to eliminate duplicate Generate pipeline in DUCasesGenerator and FieldsGenerator#257

Merged
7sharp9 merged 1 commit into
masterfrom
copilot/refactor-duplicate-code-pattern
Mar 14, 2026
Merged

Refactor: extract generateModules helper to eliminate duplicate Generate pipeline in DUCasesGenerator and FieldsGenerator#257
7sharp9 merged 1 commit into
masterfrom
copilot/refactor-duplicate-code-pattern

Conversation

Copilot AI commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

DUCasesGenerator and FieldsGenerator shared a structurally identical ~12-line pattern for parsing the AST, filtering types by attribute, and collecting generated modules — differing only in the extractor function and attribute type.

Changes

  • GeneratorHelpers.fs — two new internal helpers:

    • filterByAttribute<'A>: filters (LongIdent * SynTypeDefn list) list to namespaces containing at least one type with the given attribute
    • generateModules<'Attr>: full pipeline — parse AST → extract → filter → map to SynModuleOrNamespaceOutput.Ast
  • DUCasesGenerator.fs / FieldsGenerator.fsGenerate implementations collapsed to a single call:

// DUCasesGenerator
GeneratorHelpers.generateModules<Generator.DuCasesAttribute> context Ast.extractDU CreateDUModule.createDuModule

// FieldsGenerator
GeneratorHelpers.generateModules<Generator.FieldsAttribute> context Ast.extractRecords Create.createRecordModule
  • LensesGenerator.fs — intentionally unchanged; it processes both records and DUs in one pass and requires the SynAttribute instance for wrapper name extraction, making it incompatible with the generic helper.
Original prompt

This section details on the original issue you should resolve

<issue_title>Duplicate Code: Namespace-filtered Type Extraction Pattern in FieldsGenerator and DUCasesGenerator</issue_title>
<issue_description>Analysis of commit d8842b9

Assignee: @copilot

Summary

FieldsGenerator.fs and DUCasesGenerator.fs share a structurally identical ~12-line pattern for extracting, filtering, and collecting type definitions from the parsed AST. The only differences are the attribute type and the extraction function used. This makes the pattern a strong candidate for a shared helper.

Duplication Details

Pattern: Namespace-filtered type extraction + module collection

  • Severity: Medium
  • Occurrences: 2 instances (same structure)
  • Locations:
    • src/Myriad.Plugins/DUCasesGenerator.fs (lines 150–163)
    • src/Myriad.Plugins/FieldsGenerator.fs (lines 166–181)

DUCasesGenerator.fs:

let namespaceAndrecords =
    Ast.extractDU ast
    |> List.choose (fun (ns, types) ->
        match types |> List.filter Ast.hasAttribute(Generator.DuCasesAttribute) with
        | [] -> None
        | types -> Some (ns, types))

let modules =
    namespaceAndrecords
    |> List.collect (fun (ns, dus) ->
                        dus
                        |> List.map (fun du -> let config = Generator.getConfigFromAttribute(Generator.DuCasesAttribute) context.ConfigGetter du
                                               CreateDUModule.createDuModule ns du config))

FieldsGenerator.fs (structurally identical):

let namespaceAndrecords =
    Ast.extractRecords ast
    |> List.choose (fun (ns, types) ->
        match types |> List.filter Ast.hasAttribute(Generator.FieldsAttribute) with
        | [] -> None
        | types -> Some (ns, types))

let modules =
    namespaceAndrecords
    |> List.collect (fun (ns, records) ->
                        records
                        |> List.map (fun record -> let config = Generator.getConfigFromAttribute(Generator.FieldsAttribute) context.ConfigGetter record
                                                   let recordModule = Create.createRecordModule ns record config
                                                   recordModule))

Impact Analysis

  • Maintainability: Any change to the filtering logic (e.g., error handling, logging) must be applied in both places.
  • Bug Risk: Divergence over time — one file gets a fix or enhancement, the other does not.
  • Code Bloat: ~24 lines for what is conceptually one parameterized operation.

Refactoring Recommendations

  1. Extract a shared helper in Myriad.Plugins or Myriad.Core

    • Create a function like:
      let extractAndFilter (extractor: ParsedInput -> (LongIdent * SynTypeDefn list) list)
                           (hasAttr: SynTypeDefn -> bool)
                           (ast: ParsedInput) =
          extractor ast
          |> List.choose (fun (ns, types) ->
              match types |> List.filter hasAttr with
              | [] -> None
              | types -> Some (ns, types))
    • Then: extractAndFilter Ast.extractDU (Ast.hasAttribute(Generator.DuCasesAttribute)) ast
    • Estimated effort: 1–2 hours
    • Benefits: Single change point for filtering logic, clearer intent
  2. Alternative: Inline the collection step too

    • The namespaceAndrecords variable + modules pipeline could be collapsed into one expression using the shared helper, reducing 12 lines to ~5 in each generator.

Implementation Checklist

  • Review duplication findings
  • Prioritize refactoring tasks
  • Create refactoring plan
  • Implement changes
  • Update tests
  • Verify no functionality broken

Analysis Metadata

  • Analyzed Files: 3 (DUCasesGenerator.fs, FieldsGenerator.fs, LensesGenerator.fs)
  • Detection Method: Semantic code analysis
  • Commit: d8842b9
  • Analysis Date: 2026-03-02

Generated by Duplicate Code Detector

To install this workflow, run gh aw add githubnext/agentics/workflows/duplicate-code-detector.md@ee49512da7887942965ac0a0e48357106313c9dd. View source at https://github.com/githubnext/agentics/tree/ee49512da7887942965ac0a0e48357106313c9dd/workflows/duplicate-code-detector.md.

Comments on the Issue (you are @copilot in this section)


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI changed the title [WIP] Refactor duplicate code in FieldsGenerator and DUCasesGenerator Refactor: extract generateModules helper to eliminate duplicate Generate pipeline in DUCasesGenerator and FieldsGenerator Mar 11, 2026
@7sharp9 7sharp9 marked this pull request as ready for review March 14, 2026 16:56
Copilot AI review requested due to automatic review settings March 14, 2026 16:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot wasn't able to review any files in this pull request.


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

@7sharp9 7sharp9 merged commit d45d7fd into master Mar 14, 2026
1 check passed
@7sharp9 7sharp9 deleted the copilot/refactor-duplicate-code-pattern branch March 14, 2026 16:57
@github-actions github-actions Bot mentioned this pull request Mar 15, 2026
github-actions Bot added a commit that referenced this pull request Mar 21, 2026
…265; update date to 2026-03-21

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

Duplicate Code: Namespace-filtered Type Extraction Pattern in FieldsGenerator and DUCasesGenerator

3 participants