Skip to content

Commit f0a7c31

Browse files
committed
review(round3): trim-aware pair dedup, doc accuracy, lock duplicate-start span
Round-3 review of the foreign-regions feature: - config: dedup foreign-region marker pairs by their *trimmed* markers, matching the scanner's whole-line-after-TrimSpace comparison. A raw `==` let two pairs that differ only in surrounding whitespace (e.g. "<!-- x -->" vs " <!-- x -->") both survive, re-introducing the double MDS073 / double-protection the round-2 dedup targeted. - docs: correct foreign-regions.md — a duplicate `start` still pairs the first `start` with the next `end`, so that span *is* protected; the old "protects no bytes for it" blanket was wrong for that case. - tests: lock the duplicate-start behavior with range/span assertions on both scan paths (Scan and Restore), and add a whitespace-variant dedup test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwKxDxhjkTQPPkBrNhsrNG
1 parent b3280ef commit f0a7c31

5 files changed

Lines changed: 67 additions & 14 deletions

File tree

docs/reference/foreign-regions.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,17 @@ opaque to editing, not invisible to size accounting.
7676
## Malformed regions (MDS073)
7777

7878
APM requires each marker exactly once. mdsmith reports **MDS073** on a
79-
malformed region and protects no bytes for it:
79+
malformed region:
8080

81-
| Condition | Reported on |
82-
| ----------------------------------------------- | -------------------------- |
83-
| A `start` marker with no matching `end` | the `start` line |
84-
| An `end` marker with no preceding `start` | the `end` line |
85-
| A second `start` before the first region closes | the duplicate `start` line |
81+
| Condition | Reported on | Bytes protected |
82+
| ----------------------------------------------- | -------------------------- | -------------------------------------------- |
83+
| A `start` marker with no matching `end` | the `start` line | none |
84+
| An `end` marker with no preceding `start` | the `end` line | none |
85+
| A second `start` before the first region closes | the duplicate `start` line | the first `start` through the matching `end` |
86+
87+
An unmatched `start` or `end` protects no bytes. A duplicate `start`
88+
still pairs the first `start` with the next `end`, so that span is
89+
protected while the extra marker only draws the diagnostic.
8690

8791
## Non-goals
8892

internal/config/foreignregion.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,25 @@ func EffectiveForeignRegions(cfg *Config, filePath string) []ForeignRegion {
3737
return out
3838
}
3939

40-
// containsForeignRegion reports whether list already holds an identical
40+
// containsForeignRegion reports whether list already holds an equivalent
4141
// marker pair. EffectiveForeignRegions dedups with it so a pair declared
4242
// both top-level and on a matching override (or repeated within a list)
4343
// contributes one protected span and one MDS073 diagnostic, not two —
44-
// the check path does not otherwise dedup per-file diagnostics. The
45-
// marker-pair lists are short (a handful of entries), so the linear scan
46-
// is cheaper than allocating a set on this per-file hot path.
44+
// the check path does not otherwise dedup per-file diagnostics.
45+
//
46+
// Equivalence is by trimmed markers, matching how the scanner compares a
47+
// marker against a source line (whole-line equality after TrimSpace). Two
48+
// pairs that differ only in surrounding whitespace — e.g. "<!-- x -->" and
49+
// " <!-- x -->" — scan identically, so they must dedup to one span and one
50+
// diagnostic; a raw `==` comparison would let the whitespace variant slip
51+
// past and re-introduce the double report. The marker-pair lists are short
52+
// (a handful of entries), so the linear scan is cheaper than allocating a
53+
// set on this per-file hot path.
4754
func containsForeignRegion(list []ForeignRegion, r ForeignRegion) bool {
55+
start := strings.TrimSpace(r.Start)
56+
end := strings.TrimSpace(r.End)
4857
for _, e := range list {
49-
if e == r {
58+
if strings.TrimSpace(e.Start) == start && strings.TrimSpace(e.End) == end {
5059
return true
5160
}
5261
}

internal/config/foreignregion_more_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,28 @@ func TestEffectiveForeignRegionsDedupes(t *testing.T) {
9292
assert.Equal(t, ForeignRegion{Start: "<!-- c -->", End: "<!-- d -->"}, got[1])
9393
}
9494

95+
// TestEffectiveForeignRegionsDedupesWhitespaceVariant collapses two pairs
96+
// that differ only in surrounding marker whitespace into one entry: the
97+
// scanner compares markers by trimmed-line equality, so the variant scans
98+
// identically and must protect one span and report one MDS073, not two.
99+
func TestEffectiveForeignRegionsDedupesWhitespaceVariant(t *testing.T) {
100+
cfg := &Config{
101+
ForeignRegions: []ForeignRegion{{Start: "<!-- a -->", End: "<!-- b -->"}},
102+
Overrides: []Override{
103+
{
104+
Glob: []string{"AGENTS.md"},
105+
ForeignRegions: []ForeignRegion{
106+
// Same markers, extra surrounding whitespace.
107+
{Start: " <!-- a -->", End: "<!-- b --> "},
108+
},
109+
},
110+
},
111+
}
112+
got := EffectiveForeignRegions(cfg, "AGENTS.md")
113+
require.Len(t, got, 1)
114+
assert.Equal(t, ForeignRegion{Start: "<!-- a -->", End: "<!-- b -->"}, got[0])
115+
}
116+
95117
// TestCopyForeignRegionsNil returns nil for a nil input.
96118
func TestCopyForeignRegionsNil(t *testing.T) {
97119
assert.Nil(t, copyForeignRegions(nil))

internal/foreignregion/restore_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,15 @@ func TestRestoreMultipleRegions(t *testing.T) {
4242
got := Restore([]byte(original), []byte(fixed), []config.ForeignRegion{apm})
4343
assert.Equal(t, original, string(got))
4444
}
45+
46+
// TestRestoreDuplicateStartProtectsFirstSpan mirrors Scan's duplicate-start
47+
// pairing on the byte path: the first start pairs with the following end, so
48+
// a fixer's edit inside that span is restored even though the region is
49+
// malformed (a second start opened before it closed).
50+
func TestRestoreDuplicateStartProtectsFirstSpan(t *testing.T) {
51+
s, e := apm.Start, apm.End
52+
original := s + "\nA \n" + s + "\nB \n" + e + "\n"
53+
fixed := s + "\nA\n" + s + "\nB\n" + e + "\n"
54+
got := Restore([]byte(original), []byte(fixed), []config.ForeignRegion{apm})
55+
assert.Equal(t, original, string(got))
56+
}

internal/foreignregion/scan_test.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,20 @@ func TestScanOrphanedEnd(t *testing.T) {
6565
}
6666

6767
// TestScanDuplicateStart reports a diagnostic when a second start marker
68-
// opens before the first region closes.
68+
// opens before the first region closes, and still pairs the first start
69+
// with the following end so that span stays protected (the duplicate
70+
// marker only draws the diagnostic).
6971
func TestScanDuplicateStart(t *testing.T) {
7072
src := "<!-- apm:start -->\na\n<!-- apm:start -->\nb\n<!-- apm:end -->\n"
7173
f := newFile(t, src)
72-
_, diags := Scan(f, []config.ForeignRegion{apm})
73-
require.NotEmpty(t, diags)
74+
ranges, diags := Scan(f, []config.ForeignRegion{apm})
75+
require.Len(t, diags, 1)
7476
assert.Contains(t, diags[0].Message, "duplicate")
7577
assert.Equal(t, 3, diags[0].Line)
78+
// The first start (line 1) still pairs with the end (line 5): the
79+
// span is protected even though the region is flagged malformed.
80+
require.Len(t, ranges, 1)
81+
assert.Equal(t, lint.LineRange{From: 1, To: 5}, ranges[0])
7682
}
7783

7884
// TestScanMultiplePairs returns a range for each independent matched

0 commit comments

Comments
 (0)