-
Notifications
You must be signed in to change notification settings - Fork 15
Description
Part of duplicate code analysis: #2001
Summary
internal/difc/labels.go contains two parallel label types — SecrecyLabel and IntegrityLabel — that share nearly identical boilerplate across their constructors, flow-check methods, and Clone() implementations. The only meaningful differences are the type names and the checkSubset boolean / label-type string passed to the shared checkFlowHelper. Every structural change to the pattern must be applied twice.
Duplication Details
Pattern: Nil-guard extraction before checkFlowHelper call
- Severity: Medium
- Occurrences: 4
- Locations:
internal/difc/labels.golines ~173–182 (SecrecyLabel.CanFlowTo)internal/difc/labels.golines ~275–285 (SecrecyLabel.CheckFlow)internal/difc/labels.golines ~315–324 (IntegrityLabel.CanFlowTo)internal/difc/labels.golines ~328–338 (IntegrityLabel.CheckFlow)
- Duplicated block (6 lines × 4 = 24 lines):
var srcLabel, targetLabel *Label if l != nil { srcLabel = l.Label } if target != nil { targetLabel = target.Label }
Pattern: Structurally identical Clone() methods
- Occurrences: 2
- Locations:
internal/difc/labels.golines ~287–292 (SecrecyLabel.Clone)internal/difc/labels.golines ~340–345 (IntegrityLabel.Clone)
- Duplicated block (5 lines × 2 = 10 lines):
if l == nil || l.Label == nil { return New(Type)Label() } return &(Type)Label{Label: l.Label.Clone()}
Pattern: Identical constructors
- Occurrences: 4 (
NewSecrecyLabel,NewIntegrityLabel,NewSecrecyLabelWithTags,NewIntegrityLabelWithTags) - Each delegates to
NewLabel()/newLabelWithTags()with no variation except the return type.
Total structural duplication: ~36 lines across 10 near-identical code blocks.
Impact Analysis
- Maintainability: Any change to the nil-guard extraction, Clone guard, or constructor logic must be applied to both
SecrecyLabelandIntegrityLabel. The existingcheckFlowHelperalready consolidates the core algorithm; the remaining duplication is the boilerplate adapter code around it. - Bug Risk: Low-to-medium — if a nil-guard rule changes (e.g., add a check for
l.Label == nil), it is easy to update one and miss the other. - Code Bloat: ~36 lines that could be reduced by ~50%.
Refactoring Recommendations
-
Extract a generic
extractLabelshelper (or inline helper per file):func extractLabels[T interface{ GetLabel() *Label }](src, target T) (*Label, *Label) { var s, t *Label // use reflect or interface method to unwrap ... }
However, Go generics cannot easily express "nil receiver returns nil", so a simpler option is:
-
Add a
getLabel()method on both types and use it to consolidate the nil-guard:func (l *SecrecyLabel) getLabel() *Label { if l == nil { return nil } return l.Label } func (l *SecrecyLabel) CanFlowTo(target *SecrecyLabel) bool { ok, _ := checkFlowHelper(l.getLabel(), target.getLabel(), true, "Secrecy") return ok }
Same for
IntegrityLabel. This eliminates the 4 nil-guard blocks (24 lines → 4 one-liners). -
Simplify
Clone()using the samegetLabel()pattern:func (l *SecrecyLabel) Clone() *SecrecyLabel { if l.getLabel() == nil { return NewSecrecyLabel() } return &SecrecyLabel{Label: l.Label.Clone()} }
Implementation Checklist
- Add
getLabel() *Labelmethod toSecrecyLabelandIntegrityLabel - Refactor all 4 nil-guard extraction blocks to use
getLabel() - Refactor both
Clone()methods to usegetLabel() - Run
make testto verify no regressions - Run
make lintto ensure code style compliance
Parent Issue
See parent analysis report: #2001
Related to #2001
Generated by Duplicate Code Detector · ◷
- expires on Mar 23, 2026, 3:07 AM UTC