Support multi-symbol replace_load correctly (#1366)
The logic for removing old loading symbols replaced by `replace_load` was flawed. It would leave behind old (redundant) loads, iff the `replace_load` was to replace two symbols from the same location. This change simplifies how loads are replaced by simply keeping track of non-replaced loads rather than trying to remove from the list with index manipulation.
I've added a few more test cases. Most of these new tests will fail on the old code.
diff --git a/.bazelversion b/.bazelversion
new file mode 100644
index 0000000..2b0aa21
--- /dev/null
+++ b/.bazelversion
@@ -0,0 +1 @@
+8.2.1
diff --git a/edit/edit.go b/edit/edit.go
index e56ee45..f885ac4 100644
--- a/edit/edit.go
+++ b/edit/edit.go
@@ -1192,17 +1192,16 @@
continue
}
+ var loadTo, loadFrom []*build.Ident
for i, to := range load.To {
- if toSymbols[to.Name] {
- if i < len(load.From)-1 {
- load.From = append(load.From[:i], load.From[i+1:]...)
- load.To = append(load.To[:i], load.To[i+1:]...)
- } else {
- load.From = load.From[:i]
- load.To = load.To[:i]
- }
+ // Only add the load to the statement if it will NOT be replaced by a new load.
+ if !toSymbols[to.Name] {
+ loadTo = append(loadTo, load.To[i])
+ loadFrom = append(loadFrom, load.From[i])
}
}
+ load.To = loadTo
+ load.From = loadFrom
if len(load.To) > 0 {
all = append(all, load)
diff --git a/edit/edit_test.go b/edit/edit_test.go
index 7c22d2b..24f798e 100644
--- a/edit/edit_test.go
+++ b/edit/edit_test.go
@@ -162,42 +162,105 @@
}
func TestReplaceLoad(t *testing.T) {
- tests := []struct{ input, expected string }{
+ tests := []struct {
+ name string
+ input string
+ location string
+ from []string
+ to []string
+ expected string
+ }{
{
- ``,
- `load("new_location", "symbol")`,
+ name: "add_symbol",
+ input: ``,
+ location: "new_location",
+ from: []string{"symbol"},
+ to: []string{"symbol"},
+ expected: `load("new_location", "symbol")`,
},
{
- `load("location", "symbol")`,
- `load("new_location", "symbol")`,
+ name: "replace_location",
+ input: `load("location", "symbol")`,
+ location: "new_location",
+ from: []string{"symbol"},
+ to: []string{"symbol"},
+ expected: `load("new_location", "symbol")`,
},
{
- `load("location", "other", "symbol")`,
- `load("location", "other")
+ name: "replace_location_one_of_multiple",
+ input: `load("location", "other", "symbol")`,
+ location: "new_location",
+ from: []string{"symbol"},
+ to: []string{"symbol"},
+ expected: `load("location", "other")
load("new_location", "symbol")`,
},
{
- `load("location", symbol = "other")`,
- `load("new_location", "symbol")`,
+ name: "replace_location_alias",
+ input: `load("location", symbol = "other")`,
+ location: "new_location",
+ from: []string{"symbol"},
+ to: []string{"symbol"},
+ expected: `load("new_location", "symbol")`,
},
{
- `load("other loc", "symbol")
+ name: "collapse_duplicate_symbol",
+ input: `load("other loc", "symbol")
load("location", "symbol")`,
- `load("new_location", "symbol")`,
+ location: "new_location",
+ from: []string{"symbol"},
+ to: []string{"symbol"},
+ expected: `load("new_location", "symbol")`,
+ },
+ {
+ name: "replace_multiple_same_location",
+ input: `load("location", "symbol_a", "symbol_b", "symbol_c")`,
+ location: "new_location",
+ from: []string{"symbol_a", "symbol_b", "symbol_c"},
+ to: []string{"symbol_a", "symbol_b", "symbol_c"},
+ expected: `load("new_location", "symbol_a", "symbol_b", "symbol_c")`,
+ },
+ {
+ name: "replace_multiple_same_location_out_of_order",
+ input: `load("location", "symbol_a", "symbol_b", "symbol_c")`,
+ location: "new_location",
+ from: []string{"symbol_c", "symbol_a", "symbol_b"},
+ to: []string{"symbol_c", "symbol_a", "symbol_b"},
+ expected: `load("new_location", "symbol_a", "symbol_b", "symbol_c")`,
+ },
+ {
+ name: "replace_multiple_same_location_partial",
+ input: `load("location", "symbol_a", "symbol_b", "symbol_c")`,
+ location: "new_location",
+ from: []string{"symbol_a", "symbol_b"},
+ to: []string{"symbol_a", "symbol_b"},
+ expected: `load("location", "symbol_c")
+load("new_location", "symbol_a", "symbol_b")`,
+ },
+ {
+ name: "replace_multiple_same_location_partial_out_of_order",
+ input: `load("location", "symbol_a", "symbol_b", "symbol_c")`,
+ location: "new_location",
+ from: []string{"symbol_b", "symbol_a"},
+ to: []string{"symbol_b", "symbol_a"},
+ expected: `load("location", "symbol_c")
+load("new_location", "symbol_a", "symbol_b")`,
},
}
for _, tst := range tests {
- bld, err := build.Parse("BUILD", []byte(tst.input))
- if err != nil {
- t.Error(err)
- continue
- }
- bld.Stmt = ReplaceLoad(bld.Stmt, "new_location", []string{"symbol"}, []string{"symbol"})
- got := strings.TrimSpace(string(build.Format(bld)))
- if got != tst.expected {
- t.Errorf("maybeReplaceLoad(%s): got %s, expected %s", tst.input, got, tst.expected)
- }
+ t.Run(tst.name, func(t *testing.T) {
+ bld, err := build.Parse("BUILD", []byte(tst.input))
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ bld.Stmt = ReplaceLoad(bld.Stmt, tst.location, tst.from, tst.to)
+ got := strings.TrimSpace(string(build.Format(bld)))
+ if got != tst.expected {
+ t.Errorf("ReplaceLoad(%s): got %s, expected %s", tst.input, got, tst.expected)
+ }
+ })
}
}