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.go lines ~173–182 (SecrecyLabel.CanFlowTo)
internal/difc/labels.go lines ~275–285 (SecrecyLabel.CheckFlow)
internal/difc/labels.go lines ~315–324 (IntegrityLabel.CanFlowTo)
internal/difc/labels.go lines ~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.go lines ~287–292 (SecrecyLabel.Clone)
internal/difc/labels.go lines ~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
SecrecyLabel and IntegrityLabel. The existing checkFlowHelper already 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 extractLabels helper (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 same getLabel() pattern:
func (l *SecrecyLabel) Clone() *SecrecyLabel {
if l.getLabel() == nil { return NewSecrecyLabel() }
return &SecrecyLabel{Label: l.Label.Clone()}
}
Implementation Checklist
Parent Issue
See parent analysis report: #2001
Related to #2001
Generated by Duplicate Code Detector · ◷
Part of duplicate code analysis: #2001
Summary
internal/difc/labels.gocontains two parallel label types —SecrecyLabelandIntegrityLabel— that share nearly identical boilerplate across their constructors, flow-check methods, andClone()implementations. The only meaningful differences are the type names and thecheckSubsetboolean / label-type string passed to the sharedcheckFlowHelper. Every structural change to the pattern must be applied twice.Duplication Details
Pattern: Nil-guard extraction before
checkFlowHelpercallinternal/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)Pattern: Structurally identical
Clone()methodsinternal/difc/labels.golines ~287–292 (SecrecyLabel.Clone)internal/difc/labels.golines ~340–345 (IntegrityLabel.Clone)Pattern: Identical constructors
NewSecrecyLabel,NewIntegrityLabel,NewSecrecyLabelWithTags,NewIntegrityLabelWithTags)NewLabel()/newLabelWithTags()with no variation except the return type.Total structural duplication: ~36 lines across 10 near-identical code blocks.
Impact Analysis
SecrecyLabelandIntegrityLabel. The existingcheckFlowHelperalready consolidates the core algorithm; the remaining duplication is the boilerplate adapter code around it.l.Label == nil), it is easy to update one and miss the other.Refactoring Recommendations
Extract a generic
extractLabelshelper (or inline helper per file):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:Same for
IntegrityLabel. This eliminates the 4 nil-guard blocks (24 lines → 4 one-liners).Simplify
Clone()using the samegetLabel()pattern:Implementation Checklist
getLabel() *Labelmethod toSecrecyLabelandIntegrityLabelgetLabel()Clone()methods to usegetLabel()make testto verify no regressionsmake lintto ensure code style complianceParent Issue
See parent analysis report: #2001
Related to #2001