From 46d1f660e2f52b7d8934c2d612eb31014746dd6e Mon Sep 17 00:00:00 2001 From: Alex Torok Date: Wed, 11 Dec 2024 00:49:02 -0500 Subject: [PATCH] Add an `alias_kind` directive to configure wrapper macros (#1986) **What type of PR is this?** Feature **What package or component does this PR mostly affect?** cmd/gazelle **What does this PR do? Why is it needed?** Introduces an `alias_kind` directive to instruct gazelle for how to handle wrapper macros. Wrapper macros are starlark macros around rules that gazelle knows how to understand. A wrapper macro will have the same attrs that the underlying rule kind has, but likely does additional things like setting up common deps or generating additional targets. A common pattern for creating wrapper macros that gazelle should understand is the custom `rule.verb` usecase described here - https://bazel.build/rules/verbs-tutorial. Gazelle should still be able to understand how to index and update a wrapper macro as long as it is told what underlying rule the wrapper macro should be considered as. `alias_kind` is unique from `map_kind` in that it does not force a rule into the kind that is specified in the `alias_kind` directive. In the above "verbs" example, someone may configure a wrapper macro around a `py_binary` target, while still wanting to keep other py_binary targets around in their BUILD file. `map_kind` would not allow this, as it would force ALL py_binary targets into the mapped kind. **Which issues(s) does this PR fix?** Fixes #18 **Other notes for review** Getting this functionality required a few changes that I'll discuss here since the map_kind and wrapper macro functionality is similar, yet subtly different: 1. We now pass the wrapper macro config into the rule merger so that it can match an existing wrapper macro against the newly generated rule, even though their kinds are not strictly the same. The mapped_kind functionality did not need to do this, as the kind mapping logic in fix-update.go handles updating the generated rule to match the new kind prior to build file merging. We cannot do something similar here, as the wrapper macro pattern should not force all rules to exist in the new type. Instead, it should instruct the merger that the newly generated rule can match the wrapper macro. 2. Update the metaresolver to perform a lookup in the wrapper macro configuration. When we come across a wrapper macro, we need to use the inverseKindMapper so that we correctly update the existing rule's kind prior to sending it off to a language extension to be indexed. --- README.rst | 23 +++ cmd/gazelle/fix-update.go | 9 +- cmd/gazelle/integration_test.go | 269 ++++++++++++++++++++++++++++++++ cmd/gazelle/metaresolver.go | 60 +++++-- cmd/gazelle/update-repos.go | 5 +- config/config.go | 27 +++- merger/merger.go | 26 +-- merger/merger_test.go | 66 +++++++- 8 files changed, 454 insertions(+), 31 deletions(-) diff --git a/README.rst b/README.rst index ba48ffdb6..21a1a08a0 100644 --- a/README.rst +++ b/README.rst @@ -40,6 +40,7 @@ Gazelle build file generator .. _rules_swift_package_manager: https://github.com/cgrindel/rules_swift_package_manager .. _gazelle_rust: https://github.com/Calsign/gazelle_rust .. _rules_rust: https://github.com/bazelbuild/rules_rust +.. _Verbs Tutorial: https://bazel.build/rules/verbs-tutorial .. role:: cmd(code) .. role:: flag(code) @@ -881,6 +882,28 @@ The following directives are recognized: | | | Existing rules of the old kind will be ignored. To switch your codebase from a builtin | | kind to a mapped kind, use `buildozer`_. | ++------------------------------------------------------------+-------------------------------+ +| :direc:`# gazelle:alias_kind macro_name wrapped_kind` | n/a | ++------------------------------------------------------------+-------------------------------+ +| Denotes that a macro aliases / wraps a given rule. | +| | +| If you have a wrapper macro around a rule that gazelle knows how to update the attrs for, | +| the alias_kind directive will instruct gazelle that it should treat the particular marco | +| like the underlying wrapped kind. | +| | +| ``alias_kind`` is different from the ``map_kind`` directive in that it does not force the | +| rule to be generated as the wrapped kind. Instead, it just instructs gazelle that it | +| should index and update the attrs for rules that match the macro. | +| | +| For example, if you use ``# gazelle:alias_kind my_foo_binary foo_binary``, Gazelle will | +| still generate ``foo_binary`` targets when generating new targets from new source files. | +| It is up to a person to update the ``foo_binary`` targets to ``my_foo_binary`` targets. | +| Once this manual step is done, Gazelle will continue to update the ``my_foo_binary`` | +| targets as if they were ``foo_binary`` targets. | +| | +| Wrapper macros are commonly used to handle common boilerplate or to add deploy/release | +| verbs, as described in the bazel `Verbs Tutorial`_. | +| | +---------------------------------------------------+----------------------------------------+ | :direc:`# gazelle:prefix path` | n/a | +---------------------------------------------------+----------------------------------------+ diff --git a/cmd/gazelle/fix-update.go b/cmd/gazelle/fix-update.go index 52e6af2d5..ac3255586 100644 --- a/cmd/gazelle/fix-update.go +++ b/cmd/gazelle/fix-update.go @@ -315,6 +315,7 @@ func runFixUpdate(wd string, cmd command, args []string) (err error) { var errorsFromWalk []error walk.Walk(c, cexts, uc.dirs, uc.walkMode, func(dir, rel string, c *config.Config, update bool, f *rule.File, subdirs, regularFiles, genFiles []string) { + mrslv.AliasedKinds(rel, c.AliasMap) // If this file is ignored or if Gazelle was not asked to update this // directory, just index the build file and move on. if !update { @@ -434,7 +435,9 @@ func runFixUpdate(wd string, cmd command, args []string) (err error) { } } else { merger.MergeFile(f, empty, gen, merger.PreResolve, - unionKindInfoMaps(kinds, mappedKindInfo)) + unionKindInfoMaps(kinds, mappedKindInfo), + c.AliasMap, + ) } visits = append(visits, visitRecord{ pkgRel: rel, @@ -495,7 +498,9 @@ func runFixUpdate(wd string, cmd command, args []string) (err error) { } } merger.MergeFile(v.file, v.empty, v.rules, merger.PostResolve, - unionKindInfoMaps(kinds, v.mappedKindInfo)) + unionKindInfoMaps(kinds, v.mappedKindInfo), + v.c.AliasMap, + ) } for _, lang := range languages { if life, ok := lang.(language.LifecycleManager); ok { diff --git a/cmd/gazelle/integration_test.go b/cmd/gazelle/integration_test.go index cab375b50..e82d6af30 100644 --- a/cmd/gazelle/integration_test.go +++ b/cmd/gazelle/integration_test.go @@ -2476,6 +2476,275 @@ go_library( }) } +func TestAliasKind(t *testing.T) { + for name, tc := range map[string]struct { + before []testtools.FileSpec + after []testtools.FileSpec + index bool + }{ + "existing aliased kind with matching name has deps updated": { + index: false, + before: []testtools.FileSpec{ + { + Path: "WORKSPACE", + }, + { + Path: "BUILD.bazel", + Content: ` +# gazelle:prefix example.com/aliaskind +# gazelle:go_naming_convention go_default_library +# gazelle:alias_kind my_custom_macro go_library +load("//custom:def.bzl", "my_custom_macro") + +my_custom_macro( + name = "go_default_library", + srcs = ["file.go"], + importpath = "example.com/aliaskind", + visibility = ["//visibility:public"], +) +`, + }, + { + Path: "file.go", + Content: ` +package aliaskind + +import ( + _ "example.com/aliaskind/foo" + _ "github.com/external" +) +`, + }, + }, + after: []testtools.FileSpec{ + { + Path: "BUILD.bazel", + Content: ` +# gazelle:prefix example.com/aliaskind +# gazelle:go_naming_convention go_default_library +# gazelle:alias_kind my_custom_macro go_library +load("//custom:def.bzl", "my_custom_macro") + +my_custom_macro( + name = "go_default_library", + srcs = ["file.go"], + importpath = "example.com/aliaskind", + visibility = ["//visibility:public"], + deps = [ + "//foo:go_default_library", + "//vendor/github.com/external:go_default_library", + ], +) +`, + }, + }, + }, + "existing aliased kind with different name has deps updated": { + index: false, + before: []testtools.FileSpec{ + { + Path: "WORKSPACE", + }, + { + Path: "BUILD.bazel", + Content: ` +# gazelle:prefix example.com/aliaskind +# gazelle:go_naming_convention go_default_library +# gazelle:alias_kind my_custom_macro go_library +load("//custom:def.bzl", "my_custom_macro") + +my_custom_macro( + name = "custom_lib", + srcs = ["file.go"], + importpath = "example.com/aliaskind", + visibility = ["//visibility:public"], +) +`, + }, + { + Path: "file.go", + Content: ` +package aliaskind + +import ( + _ "example.com/aliaskind/foo" + _ "github.com/external" +) +`, + }, + }, + after: []testtools.FileSpec{ + { + Path: "BUILD.bazel", + Content: ` +# gazelle:prefix example.com/aliaskind +# gazelle:go_naming_convention go_default_library +# gazelle:alias_kind my_custom_macro go_library +load("//custom:def.bzl", "my_custom_macro") + +my_custom_macro( + name = "custom_lib", + srcs = ["file.go"], + importpath = "example.com/aliaskind", + visibility = ["//visibility:public"], + deps = [ + "//foo:go_default_library", + "//vendor/github.com/external:go_default_library", + ], +) +`, + }, + }, + }, + "existing aliased kind is indexed for deps": { + index: true, + before: []testtools.FileSpec{ + { + Path: "WORKSPACE", + }, + { + Path: "BUILD.bazel", + Content: ` +load("@io_bazel_rules_go//go:def.bzl", "go_library") +# gazelle:prefix example.com/aliaskind +# gazelle:go_naming_convention go_default_library +# gazelle:alias_kind my_custom_macro go_library + +go_library( + name = "go_default_library", + srcs = ["file.go"], + importpath = "example.com/aliaskind", + visibility = ["//visibility:public"], +) + `, + }, + { + Path: "file.go", + Content: ` +package aliaskind + +import ( + _ "example.com/aliaskind/foo" + _ "github.com/external" +) + `, + }, + { + Path: "foo/BUILD.bazel", + Content: ` +load("//custom:def.bzl", "my_custom_macro") + +my_custom_macro( + name = "go_default_library", + srcs = ["foo.go"], + importpath = "example.com/aliaskind/foo", + visibility = ["//visibility:public"], +) + `, + }, + { + Path: "foo/foo.go", + Content: "package foo", + }, + }, + after: []testtools.FileSpec{ + { + Path: "BUILD.bazel", + Content: ` +load("@io_bazel_rules_go//go:def.bzl", "go_library") +# gazelle:prefix example.com/aliaskind +# gazelle:go_naming_convention go_default_library +# gazelle:alias_kind my_custom_macro go_library + +go_library( + name = "go_default_library", + srcs = ["file.go"], + importpath = "example.com/aliaskind", + visibility = ["//visibility:public"], + deps = [ + "//foo:go_default_library", + "//vendor/github.com/external:go_default_library", + ], +) + `, + }, + }, + }, + "alias_kind around a mapped_kind": { + index: false, + before: []testtools.FileSpec{ + { + Path: "WORKSPACE", + }, + { + Path: "BUILD.bazel", + Content: ` +# gazelle:prefix example.com/aliaskind +# gazelle:go_naming_convention go_default_library +# gazelle:map_kind go_library my_library //tools:go:def.bzl +# gazelle:alias_kind my_custom_macro my_library +load("//custom:def.bzl", "my_custom_macro") + +my_custom_macro( + name = "go_default_library", + srcs = ["file.go"], + importpath = "example.com/aliaskind", + visibility = ["//visibility:public"], +) +`, + }, + { + Path: "file.go", + Content: ` +package aliaskind + +import ( + _ "example.com/aliaskind/foo" + _ "github.com/external" +) +`, + }, + }, + after: []testtools.FileSpec{ + { + Path: "BUILD.bazel", + Content: ` +# gazelle:prefix example.com/aliaskind +# gazelle:go_naming_convention go_default_library +# gazelle:map_kind go_library my_library //tools:go:def.bzl +# gazelle:alias_kind my_custom_macro my_library +load("//custom:def.bzl", "my_custom_macro") + +my_custom_macro( + name = "go_default_library", + srcs = ["file.go"], + importpath = "example.com/aliaskind", + visibility = ["//visibility:public"], + deps = [ + "//foo:go_default_library", + "//vendor/github.com/external:go_default_library", + ], +) +`, + }, + }, + }, + } { + t.Run(name, func(t *testing.T) { + dir, cleanup := testtools.CreateFiles(t, tc.before) + t.Cleanup(cleanup) + args := []string{"-external=vendored"} + if !tc.index { + args = append(args, "-index=false") + } + if err := runGazelle(dir, args); err != nil { + t.Fatal(err) + } + testtools.CheckFiles(t, dir, tc.after) + }) + } +} + func TestMapKindEdgeCases(t *testing.T) { for name, tc := range map[string]struct { before []testtools.FileSpec diff --git a/cmd/gazelle/metaresolver.go b/cmd/gazelle/metaresolver.go index f289947ef..6fd541bf3 100644 --- a/cmd/gazelle/metaresolver.go +++ b/cmd/gazelle/metaresolver.go @@ -30,12 +30,16 @@ type metaResolver struct { // mappedKinds provides a list of replacements used by File.Pkg. mappedKinds map[string][]config.MappedKind + + // aliasedKinds provides a dict of configured wrapper macros for each package + aliasedKinds map[string]map[string]string } func newMetaResolver() *metaResolver { return &metaResolver{ - builtins: make(map[string]resolve.Resolver), - mappedKinds: make(map[string][]config.MappedKind), + builtins: make(map[string]resolve.Resolver), + mappedKinds: make(map[string][]config.MappedKind), + aliasedKinds: make(map[string]map[string]string), } } @@ -50,23 +54,55 @@ func (mr *metaResolver) MappedKind(pkgRel string, kind config.MappedKind) { mr.mappedKinds[pkgRel] = append(mr.mappedKinds[pkgRel], kind) } +// AliasedKinds records the configured wrapper macros for a package +func (mr *metaResolver) AliasedKinds(pkgRel string, aliasedKinds map[string]string) { + // Note: it is somewhat of a hack to store the aliased kinds in the metaResolver + // by each package. A more appropriate place for this would be to keep it in the + // config.Config struct. However, the config.Config struct is not available at + // all of the call sites where the Resolve method is called. + // + // For example, when the RuleIndex is finalizing and collecting information about + // embedded targets, it does this once across the entire index. + mr.aliasedKinds[pkgRel] = aliasedKinds +} + // Resolver returns a resolver for the given rule and package, and a bool // indicating whether one was found. Empty string may be passed for pkgRel, // which results in consulting the builtin kinds only. func (mr *metaResolver) Resolver(r *rule.Rule, pkgRel string) resolve.Resolver { + ruleKind := r.Kind() + + if wrappedKind, ok := mr.aliasedKinds[pkgRel][ruleKind]; ok { + ruleKind = wrappedKind + } + + // Once we have checked alias kinds, still look through our mapped kinds so that we can handle + // an aliased kind that points to a mapped kind: + // e.g other_macro should use the go_library resolver here: + // # gazelle:map_kind my_go_library go_library //:foo.bzl + // # gazelle:alias_kind other_macro my_go_library for _, mappedKind := range mr.mappedKinds[pkgRel] { - if mappedKind.KindName == r.Kind() { - fromKindResolver := mr.builtins[mappedKind.FromKind] - if fromKindResolver == nil { - return nil - } - return inverseMapKindResolver{ - fromKind: mappedKind.FromKind, - delegate: fromKindResolver, - } + if mappedKind.KindName == ruleKind { + ruleKind = mappedKind.FromKind + break } } - return mr.builtins[r.Kind()] + + // If the underlying kind is different, we need to apply the inverse map_kind operation so that + // we get the Resolver for the underlying kind, not the mapped or aliased one that we see in the + // existing BUILD file. + if ruleKind != r.Kind() { + fromKindResolver := mr.builtins[ruleKind] + if fromKindResolver == nil { + return nil + } + return inverseMapKindResolver{ + fromKind: ruleKind, + delegate: fromKindResolver, + } + } + + return mr.builtins[ruleKind] } // inverseMapKindResolver applies an inverse of the map_kind diff --git a/cmd/gazelle/update-repos.go b/cmd/gazelle/update-repos.go index 8c4a168da..3a418a0d1 100644 --- a/cmd/gazelle/update-repos.go +++ b/cmd/gazelle/update-repos.go @@ -304,7 +304,10 @@ func updateRepos(wd string, args []string) (err error) { updatedFiles := make(map[string]*rule.File) for _, f := range sortedFiles { - merger.MergeFile(f, emptyForFiles[f], genForFiles[f], merger.PreResolve, kinds) + // We don't need to pass any wrapper macros config into MergeFile because the update repos command does not support + // the '# gazelle:alias_kind MACRO KIND' directive. + var emptyAliasedKinds map[string]string = nil + merger.MergeFile(f, emptyForFiles[f], genForFiles[f], merger.PreResolve, kinds, emptyAliasedKinds) merger.FixLoads(f, loads) if f == uc.workspace && !c.Bzlmod { if err := merger.CheckGazelleLoaded(f); err != nil { diff --git a/config/config.go b/config/config.go index fed885db3..5aae20c0e 100644 --- a/config/config.go +++ b/config/config.go @@ -91,6 +91,12 @@ type Config struct { // # gazelle:map_kind. KindMap map[string]MappedKind + // AliasMap maps a wrapper macro name to the kind of rule that it wraps. + // It provides a way for users to define custom macros that generate rules + // that are understood by gazelle, while still allowing gazelle to update + // the attrs for the macro calls. Configured via # gazelle:macro. + AliasMap map[string]string + // Repos is a list of repository rules declared in the main WORKSPACE file // or in macros called by the main WORKSPACE file. This may affect rule // generation and dependency resolution. @@ -266,7 +272,7 @@ func (cc *CommonConfigurer) CheckFlags(fs *flag.FlagSet, c *Config) error { } func (cc *CommonConfigurer) KnownDirectives() []string { - return []string{"build_file_name", "map_kind", "lang"} + return []string{"build_file_name", "map_kind", "alias_kind", "lang"} } func (cc *CommonConfigurer) Configure(c *Config, rel string, f *rule.File) { @@ -293,6 +299,25 @@ func (cc *CommonConfigurer) Configure(c *Config, rel string, f *rule.File) { KindLoad: vals[2], } + case "alias_kind": + vals := strings.Fields(d.Value) + if len(vals) != 2 { + log.Printf("expected two arguments (gazelle:alias_kind alias_kind underlying_kind), got %v", vals) + continue + } + + aliasName := vals[0] + underlyingKind := vals[1] + if aliasName == underlyingKind { + log.Printf("alias_kind: alias kind %q is the same as the underlying kind %q", aliasName, underlyingKind) + continue + } + + if c.AliasMap == nil { + c.AliasMap = make(map[string]string) + } + c.AliasMap[aliasName] = underlyingKind + case "lang": if len(d.Value) > 0 { c.Langs = strings.Split(d.Value, ",") diff --git a/merger/merger.go b/merger/merger.go index b1827d18c..29f39b331 100644 --- a/merger/merger.go +++ b/merger/merger.go @@ -106,7 +106,7 @@ const UnstableInsertIndexKey = "_gazelle_insert_index" // If an attribute is marked with a "# keep" comment, it will not be merged. // If a rule is marked with a "# keep" comment, the whole rule will not // be modified. -func MergeFile(oldFile *rule.File, emptyRules, genRules []*rule.Rule, phase Phase, kinds map[string]rule.KindInfo) { +func MergeFile(oldFile *rule.File, emptyRules, genRules []*rule.Rule, phase Phase, kinds map[string]rule.KindInfo, aliasedKinds map[string]string) { getMergeAttrs := func(r *rule.Rule) map[string]bool { if phase == PreResolve { return kinds[r.Kind()].MergeableAttrs @@ -117,7 +117,7 @@ func MergeFile(oldFile *rule.File, emptyRules, genRules []*rule.Rule, phase Phas // Merge empty rules into the file and delete any rules which become empty. for _, emptyRule := range emptyRules { - if oldRule, _ := match(oldFile.Rules, emptyRule, kinds[emptyRule.Kind()], false); oldRule != nil { + if oldRule, _ := match(oldFile.Rules, emptyRule, kinds[emptyRule.Kind()], false, aliasedKinds); oldRule != nil { if oldRule.ShouldKeep() { continue } @@ -135,7 +135,7 @@ func MergeFile(oldFile *rule.File, emptyRules, genRules []*rule.Rule, phase Phas matchErrors := make([]error, len(genRules)) substitutions := make(map[string]string) for i, genRule := range genRules { - oldRule, err := Match(oldFile.Rules, genRule, kinds[genRule.Kind()]) + oldRule, err := Match(oldFile.Rules, genRule, kinds[genRule.Kind()], aliasedKinds) if err != nil { // TODO(jayconrod): add a verbose mode and log errors. They are too chatty // to print by default. @@ -210,11 +210,11 @@ func substituteRule(r *rule.Rule, substitutions map[string]string, info rule.Kin // the quality of the match (name match is best, then attribute match in the // order that attributes are listed). If disambiguation is successful, // the rule and nil are returned. Otherwise, nil and an error are returned. -func Match(rules []*rule.Rule, x *rule.Rule, info rule.KindInfo) (*rule.Rule, error) { - return match(rules, x, info, true) +func Match(rules []*rule.Rule, x *rule.Rule, info rule.KindInfo, aliasedKinds map[string]string) (*rule.Rule, error) { + return match(rules, x, info, true, aliasedKinds) } -func match(rules []*rule.Rule, x *rule.Rule, info rule.KindInfo, wantError bool) (*rule.Rule, error) { +func match(rules []*rule.Rule, x *rule.Rule, info rule.KindInfo, wantError bool, aliasedKinds map[string]string) (*rule.Rule, error) { xname := x.Name() xkind := x.Kind() var nameMatches []*rule.Rule @@ -223,20 +223,20 @@ func match(rules []*rule.Rule, x *rule.Rule, info rule.KindInfo, wantError bool) if xname == y.Name() { nameMatches = append(nameMatches, y) } - if xkind == y.Kind() { + if xkind == y.Kind() || aliasedKinds[y.Kind()] == xkind { kindMatches = append(kindMatches, y) } } if len(nameMatches) == 1 { y := nameMatches[0] - if xkind != y.Kind() { - if !wantError { - return nil, nil - } - return nil, fmt.Errorf("could not merge %s(%s): a rule of the same name has kind %s", xkind, xname, y.Kind()) + if xkind == y.Kind() || xkind == aliasedKinds[y.Kind()] { + return y, nil + } + if !wantError { + return nil, nil } - return y, nil + return nil, fmt.Errorf("could not merge %s(%s): a rule of the same name has kind %s", xkind, xname, y.Kind()) } if len(nameMatches) > 1 { if !wantError { diff --git a/merger/merger_test.go b/merger/merger_test.go index 704eb95ac..4c4dc3bd8 100644 --- a/merger/merger_test.go +++ b/merger/merger_test.go @@ -33,6 +33,7 @@ import ( type testCase struct { desc, previous, current, empty, expected string + aliasedKinds map[string]string } var testCases = []testCase{ @@ -927,6 +928,50 @@ selects.config_setting_group( ], ) `, + }, { + desc: "aliased kind with new underlying kind", + previous: ` +load("//:defs.bzl", "my_go_library") + +my_go_library( + name = "go_default_library", + srcs = ["lib.go"], +) +`, + current: ` +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["lib.go"], + deps = [":dep"], +) + +go_library( + name = "another_lib", + srcs = ["another.go"], + deps = [":dep"], +) +`, + expected: ` +load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("//:defs.bzl", "my_go_library") + +my_go_library( + name = "go_default_library", + srcs = ["lib.go"], + deps = [":dep"], +) + +go_library( + name = "another_lib", + srcs = ["another.go"], + deps = [":dep"], +) +`, + aliasedKinds: map[string]string{ + "my_go_library": "go_library", + }, }, } @@ -945,7 +990,7 @@ func TestMergeFile(t *testing.T) { if err != nil { t.Fatalf("%s: %v", tc.desc, err) } - merger.MergeFile(f, emptyFile.Rules, genFile.Rules, merger.PreResolve, testKinds) + merger.MergeFile(f, emptyFile.Rules, genFile.Rules, merger.PreResolve, testKinds, tc.aliasedKinds) merger.FixLoads(f, testLoads) want := tc.expected @@ -999,6 +1044,7 @@ func TestMatch(t *testing.T) { desc, gen, old string wantIndex int wantError bool + aliasedKinds map[string]string }{ { desc: "no_match", @@ -1054,6 +1100,22 @@ go_binary(name = "z") gen: `selects.config_setting_group(name = "conf_group_1", match_any = ["//:config_a", "//:config_b"])`, old: `selects.config_setting_group(name = "conf_group_1", match_any = ["//:config_c", "//:config_d"])`, wantIndex: 0, + }, { + desc: "wrapper macro name match", + gen: `go_library(name = "lib", srcs = ["lib.go"])`, + old: `custom_go_library(name = "lib", srcs = ["lib.go"])`, + wantIndex: 0, + aliasedKinds: map[string]string{ + "custom_go_library": "go_library", + }, + }, { + desc: "wrapper macro importpath match", + gen: `go_library(name = "lib", srcs = ["lib.go"], importpath = "example.com/repo/foo")`, + old: `custom_go_library(name = "old_lib", srcs = ["lib.go"], importpath = "example.com/repo/foo")`, + wantIndex: 0, + aliasedKinds: map[string]string{ + "custom_go_library": "go_library", + }, }, } { t.Run(tc.desc, func(t *testing.T) { @@ -1067,7 +1129,7 @@ go_binary(name = "z") } r := genFile.Rules[0] info := testKinds[r.Kind()] - if got, gotErr := merger.Match(oldFile.Rules, r, info); gotErr != nil { + if got, gotErr := merger.Match(oldFile.Rules, r, info, tc.aliasedKinds); gotErr != nil { if !tc.wantError { t.Errorf("unexpected error: %v", gotErr) }