github.com/afking/bazel-gazelle@v0.0.0-20180301150245-c02bc0f529e8/internal/merger/merger.go (about)

     1  /* Copyright 2016 The Bazel Authors. All rights reserved.
     2  
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7     http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  // Package merger provides methods for merging parsed BUILD files.
    17  package merger
    18  
    19  import (
    20  	"fmt"
    21  	"log"
    22  	"sort"
    23  	"strings"
    24  
    25  	"github.com/bazelbuild/bazel-gazelle/internal/config"
    26  	"github.com/bazelbuild/bazel-gazelle/internal/label"
    27  	bf "github.com/bazelbuild/buildtools/build"
    28  )
    29  
    30  const keep = "keep" // marker in srcs or deps to tell gazelle to preserve.
    31  
    32  // MergableAttrs is the set of attribute names for each kind of rule that
    33  // may be merged. When an attribute is mergeable, a generated value may
    34  // replace or augment an existing value. If an attribute is not mergeable,
    35  // existing values are preserved. Generated non-mergeable attributes may
    36  // still be added to a rule if there is no corresponding existing attribute.
    37  type MergeableAttrs map[string]map[string]bool
    38  
    39  var (
    40  	// PreResolveAttrs is the set of attributes that should be merged before
    41  	// dependency resolution, i.e., everything except deps.
    42  	PreResolveAttrs MergeableAttrs
    43  
    44  	// PostResolveAttrs is the set of attributes that should be merged after
    45  	// dependency resolution, i.e., deps.
    46  	PostResolveAttrs MergeableAttrs
    47  
    48  	// RepoAttrs is the set of attributes that should be merged in repository
    49  	// rules in WORKSPACE.
    50  	RepoAttrs MergeableAttrs
    51  
    52  	// nonEmptyAttrs is the set of attributes that disqualify a rule from being
    53  	// deleted after merge.
    54  	nonEmptyAttrs MergeableAttrs
    55  )
    56  
    57  func init() {
    58  	PreResolveAttrs = make(MergeableAttrs)
    59  	PostResolveAttrs = make(MergeableAttrs)
    60  	RepoAttrs = make(MergeableAttrs)
    61  	nonEmptyAttrs = make(MergeableAttrs)
    62  	for _, set := range []struct {
    63  		mergeableAttrs MergeableAttrs
    64  		kinds, attrs   []string
    65  	}{
    66  		{
    67  			mergeableAttrs: PreResolveAttrs,
    68  			kinds: []string{
    69  				"go_library",
    70  				"go_binary",
    71  				"go_test",
    72  				"go_proto_library",
    73  				"proto_library",
    74  			},
    75  			attrs: []string{
    76  				"srcs",
    77  			},
    78  		}, {
    79  			mergeableAttrs: PreResolveAttrs,
    80  			kinds: []string{
    81  				"go_library",
    82  				"go_proto_library",
    83  			},
    84  			attrs: []string{
    85  				"importpath",
    86  			},
    87  		}, {
    88  			mergeableAttrs: PreResolveAttrs,
    89  			kinds: []string{
    90  				"go_library",
    91  				"go_binary",
    92  				"go_test",
    93  				"go_proto_library",
    94  			},
    95  			attrs: []string{
    96  				"cgo",
    97  				"clinkopts",
    98  				"copts",
    99  				"embed",
   100  			},
   101  		}, {
   102  			mergeableAttrs: PreResolveAttrs,
   103  			kinds: []string{
   104  				"go_proto_library",
   105  			},
   106  			attrs: []string{
   107  				"proto",
   108  			},
   109  		}, {
   110  			mergeableAttrs: PostResolveAttrs,
   111  			kinds: []string{
   112  				"go_library",
   113  				"go_binary",
   114  				"go_test",
   115  				"go_proto_library",
   116  				"proto_library",
   117  			},
   118  			attrs: []string{
   119  				"deps",
   120  				config.GazelleImportsKey,
   121  			},
   122  		}, {
   123  			mergeableAttrs: RepoAttrs,
   124  			kinds: []string{
   125  				"go_repository",
   126  			},
   127  			attrs: []string{
   128  				"commit",
   129  				"importpath",
   130  				"remote",
   131  				"sha256",
   132  				"strip_prefix",
   133  				"tag",
   134  				"type",
   135  				"urls",
   136  				"vcs",
   137  			},
   138  		}, {
   139  			mergeableAttrs: nonEmptyAttrs,
   140  			kinds: []string{
   141  				"go_binary",
   142  				"go_library",
   143  				"go_test",
   144  				"proto_library",
   145  			},
   146  			attrs: []string{
   147  				"srcs",
   148  				"deps",
   149  			},
   150  		}, {
   151  			mergeableAttrs: nonEmptyAttrs,
   152  			kinds: []string{
   153  				"go_binary",
   154  				"go_library",
   155  				"go_test",
   156  			},
   157  			attrs: []string{
   158  				"embed",
   159  			},
   160  		}, {
   161  			mergeableAttrs: nonEmptyAttrs,
   162  			kinds: []string{
   163  				"go_proto_library",
   164  			},
   165  			attrs: []string{
   166  				"proto",
   167  			},
   168  		},
   169  	} {
   170  		for _, kind := range set.kinds {
   171  			if set.mergeableAttrs[kind] == nil {
   172  				set.mergeableAttrs[kind] = make(map[string]bool)
   173  			}
   174  			for _, attr := range set.attrs {
   175  				set.mergeableAttrs[kind][attr] = true
   176  			}
   177  		}
   178  	}
   179  }
   180  
   181  // MergeFile merges the rules in genRules with matching rules in oldFile and
   182  // adds unmatched rules to the end of the merged file. MergeFile also merges
   183  // rules in empty with matching rules in oldFile and deletes rules that
   184  // are empty after merging. attrs is the set of attributes to merge. Attributes
   185  // not in this set will be left alone if they already exist.
   186  func MergeFile(genRules []bf.Expr, empty []bf.Expr, oldFile *bf.File, attrs MergeableAttrs) (mergedFile *bf.File, mergedRules []bf.Expr) {
   187  	// Merge empty rules into the file and delete any rules which become empty.
   188  	mergedFile = new(bf.File)
   189  	*mergedFile = *oldFile
   190  	mergedFile.Stmt = append([]bf.Expr{}, oldFile.Stmt...)
   191  	var deletedIndices []int
   192  	for _, s := range empty {
   193  		emptyCall := s.(*bf.CallExpr)
   194  		if oldCall, i, _ := match(oldFile.Stmt, emptyCall); oldCall != nil {
   195  			mergedRule := mergeRule(emptyCall, oldCall, attrs, oldFile.Path)
   196  			if isRuleEmpty(mergedRule) {
   197  				deletedIndices = append(deletedIndices, i)
   198  			} else {
   199  				mergedFile.Stmt[i] = mergedRule
   200  			}
   201  		}
   202  	}
   203  	if len(deletedIndices) > 0 {
   204  		sort.Ints(deletedIndices)
   205  		mergedFile.Stmt = deleteIndices(mergedFile.Stmt, deletedIndices)
   206  	}
   207  
   208  	// Match generated rules with existing rules in the file. Keep track of
   209  	// rules with non-standard names.
   210  	matchIndices := make([]int, len(genRules))
   211  	matchErrors := make([]error, len(genRules))
   212  	substitutions := make(map[string]string)
   213  	for i, s := range genRules {
   214  		genCall := s.(*bf.CallExpr)
   215  		oldCall, oldIndex, err := match(mergedFile.Stmt, genCall)
   216  		if err != nil {
   217  			// TODO(jayconrod): add a verbose mode and log errors. They are too chatty
   218  			// to print by default.
   219  			matchErrors[i] = err
   220  			continue
   221  		}
   222  		matchIndices[i] = oldIndex // < 0 indicates no match
   223  		if oldCall != nil {
   224  			oldRule := bf.Rule{Call: oldCall}
   225  			genRule := bf.Rule{Call: genCall}
   226  			oldName := oldRule.Name()
   227  			genName := genRule.Name()
   228  			if oldName != genName {
   229  				substitutions[genName] = oldName
   230  			}
   231  		}
   232  	}
   233  
   234  	// Rename labels in generated rules that refer to other generated rules.
   235  	if len(substitutions) > 0 {
   236  		genRules = append([]bf.Expr{}, genRules...)
   237  		for i, s := range genRules {
   238  			genRules[i] = substituteRule(s.(*bf.CallExpr), substitutions)
   239  		}
   240  	}
   241  
   242  	// Merge generated rules with existing rules or append to the end of the file.
   243  	for i := range genRules {
   244  		if matchErrors[i] != nil {
   245  			continue
   246  		}
   247  		if matchIndices[i] < 0 {
   248  			mergedFile.Stmt = append(mergedFile.Stmt, genRules[i])
   249  			mergedRules = append(mergedRules, genRules[i])
   250  		} else {
   251  			mergedRule := mergeRule(genRules[i].(*bf.CallExpr), mergedFile.Stmt[matchIndices[i]].(*bf.CallExpr), attrs, oldFile.Path)
   252  			mergedFile.Stmt[matchIndices[i]] = mergedRule
   253  			mergedRules = append(mergedRules, mergedRule)
   254  		}
   255  	}
   256  
   257  	return mergedFile, mergedRules
   258  }
   259  
   260  // mergeRule combines information from gen and old and returns an updated rule.
   261  // Both rules must be non-nil and must have the same kind and same name.
   262  // attrs is the set of attributes which may be merged.
   263  // If nil is returned, the rule should be deleted.
   264  func mergeRule(gen, old *bf.CallExpr, attrs MergeableAttrs, filename string) bf.Expr {
   265  	if old != nil && shouldKeep(old) {
   266  		return old
   267  	}
   268  
   269  	genRule := bf.Rule{Call: gen}
   270  	oldRule := bf.Rule{Call: old}
   271  	merged := *old
   272  	merged.List = nil
   273  	mergedRule := bf.Rule{Call: &merged}
   274  
   275  	// Copy unnamed arguments from the old rule without merging. The only rule
   276  	// generated with unnamed arguments is go_prefix, which we currently
   277  	// leave in place.
   278  	// TODO: maybe gazelle should allow the prefix to be changed.
   279  	for _, a := range old.List {
   280  		if b, ok := a.(*bf.BinaryExpr); ok && b.Op == "=" {
   281  			break
   282  		}
   283  		merged.List = append(merged.List, a)
   284  	}
   285  
   286  	// Merge attributes from the old rule. Preserve comments on old attributes.
   287  	// Assume generated attributes have no comments.
   288  	kind := oldRule.Kind()
   289  	for _, k := range oldRule.AttrKeys() {
   290  		oldAttr := oldRule.AttrDefn(k)
   291  		if !attrs[kind][k] || shouldKeep(oldAttr) {
   292  			merged.List = append(merged.List, oldAttr)
   293  			continue
   294  		}
   295  
   296  		oldExpr := oldAttr.Y
   297  		genExpr := genRule.Attr(k)
   298  		mergedExpr, err := mergeExpr(genExpr, oldExpr)
   299  		if err != nil {
   300  			start, end := oldExpr.Span()
   301  			log.Printf("%s:%d.%d-%d.%d: could not merge expression", filename, start.Line, start.LineRune, end.Line, end.LineRune)
   302  			mergedExpr = oldExpr
   303  		}
   304  		if mergedExpr != nil {
   305  			mergedAttr := *oldAttr
   306  			mergedAttr.Y = mergedExpr
   307  			merged.List = append(merged.List, &mergedAttr)
   308  		}
   309  	}
   310  
   311  	// Merge attributes from genRule that we haven't processed already.
   312  	for _, k := range genRule.AttrKeys() {
   313  		if mergedRule.Attr(k) == nil {
   314  			mergedRule.SetAttr(k, genRule.Attr(k))
   315  		}
   316  	}
   317  
   318  	return &merged
   319  }
   320  
   321  // mergeExpr combines information from gen and old and returns an updated
   322  // expression. The following kinds of expressions are recognized:
   323  //
   324  //   * nil
   325  //   * strings (can only be merged with strings)
   326  //   * lists of strings
   327  //   * a call to select with a dict argument. The dict keys must be strings,
   328  //     and the values must be lists of strings.
   329  //   * a list of strings combined with a select call using +. The list must
   330  //     be the left operand.
   331  //
   332  // An error is returned if the expressions can't be merged, for example
   333  // because they are not in one of the above formats.
   334  func mergeExpr(gen, old bf.Expr) (bf.Expr, error) {
   335  	if shouldKeep(old) {
   336  		return old, nil
   337  	}
   338  	if gen == nil && (old == nil || isScalar(old)) {
   339  		return nil, nil
   340  	}
   341  	if isScalar(gen) {
   342  		return gen, nil
   343  	}
   344  
   345  	genExprs, err := extractPlatformStringsExprs(gen)
   346  	if err != nil {
   347  		return nil, err
   348  	}
   349  	oldExprs, err := extractPlatformStringsExprs(old)
   350  	if err != nil {
   351  		return nil, err
   352  	}
   353  	mergedExprs, err := mergePlatformStringsExprs(genExprs, oldExprs)
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  	return makePlatformStringsExpr(mergedExprs), nil
   358  }
   359  
   360  // platformStringsExprs is a set of sub-expressions that match the structure
   361  // of package.PlatformStrings. rules.Generator produces expressions that
   362  // follow this structure for srcs, deps, and other attributes, so this matches
   363  // all non-scalar expressions generated by Gazelle.
   364  //
   365  // The matched expression has the form:
   366  //
   367  // [] + select({}) + select({}) + select({})
   368  //
   369  // The four collections may appear in any order, and some or all of them may
   370  // be omitted (all fields are nil for a nil expression).
   371  type platformStringsExprs struct {
   372  	generic            *bf.ListExpr
   373  	os, arch, platform *bf.DictExpr
   374  }
   375  
   376  // extractPlatformStringsExprs matches an expression and attempts to extract
   377  // sub-expressions in platformStringsExprs. The sub-expressions can then be
   378  // merged with corresponding sub-expressions. Any field in the returned
   379  // structure may be nil. An error is returned if the given expression does
   380  // not follow the pattern described by platformStringsExprs.
   381  func extractPlatformStringsExprs(expr bf.Expr) (platformStringsExprs, error) {
   382  	var ps platformStringsExprs
   383  	if expr == nil {
   384  		return ps, nil
   385  	}
   386  
   387  	// Break the expression into a sequence of expressions combined with +.
   388  	var parts []bf.Expr
   389  	for {
   390  		binop, ok := expr.(*bf.BinaryExpr)
   391  		if !ok {
   392  			parts = append(parts, expr)
   393  			break
   394  		}
   395  		parts = append(parts, binop.Y)
   396  		expr = binop.X
   397  	}
   398  
   399  	// Process each part. They may be in any order.
   400  	for _, part := range parts {
   401  		switch part := part.(type) {
   402  		case *bf.ListExpr:
   403  			if ps.generic != nil {
   404  				return platformStringsExprs{}, fmt.Errorf("expression could not be matched: multiple list expressions")
   405  			}
   406  			ps.generic = part
   407  
   408  		case *bf.CallExpr:
   409  			x, ok := part.X.(*bf.LiteralExpr)
   410  			if !ok || x.Token != "select" || len(part.List) != 1 {
   411  				return platformStringsExprs{}, fmt.Errorf("expression could not be matched: callee other than select or wrong number of args")
   412  			}
   413  			arg, ok := part.List[0].(*bf.DictExpr)
   414  			if !ok {
   415  				return platformStringsExprs{}, fmt.Errorf("expression could not be matched: select argument not dict")
   416  			}
   417  			var dict **bf.DictExpr
   418  			for _, item := range arg.List {
   419  				kv := item.(*bf.KeyValueExpr) // parser guarantees this
   420  				k, ok := kv.Key.(*bf.StringExpr)
   421  				if !ok {
   422  					return platformStringsExprs{}, fmt.Errorf("expression could not be matched: dict keys are not all strings")
   423  				}
   424  				if k.Value == "//conditions:default" {
   425  					continue
   426  				}
   427  				key, err := label.Parse(k.Value)
   428  				if err != nil {
   429  					return platformStringsExprs{}, fmt.Errorf("expression could not be matched: dict key is not label: %q", k.Value)
   430  				}
   431  				if config.KnownOSSet[key.Name] {
   432  					dict = &ps.os
   433  					break
   434  				}
   435  				if config.KnownArchSet[key.Name] {
   436  					dict = &ps.arch
   437  					break
   438  				}
   439  				osArch := strings.Split(key.Name, "_")
   440  				if len(osArch) != 2 || !config.KnownOSSet[osArch[0]] || !config.KnownArchSet[osArch[1]] {
   441  					return platformStringsExprs{}, fmt.Errorf("expression could not be matched: dict key contains unknown platform: %q", k.Value)
   442  				}
   443  				dict = &ps.platform
   444  				break
   445  			}
   446  			if dict == nil {
   447  				// We could not identify the dict because it's empty or only contains
   448  				// //conditions:default. We'll call it the platform dict to avoid
   449  				// dropping it.
   450  				dict = &ps.platform
   451  			}
   452  			if *dict != nil {
   453  				return platformStringsExprs{}, fmt.Errorf("expression could not be matched: multiple selects that are either os-specific, arch-specific, or platform-specific")
   454  			}
   455  			*dict = arg
   456  		}
   457  	}
   458  	return ps, nil
   459  }
   460  
   461  // makePlatformStringsExpr constructs a single expression from the
   462  // sub-expressions in ps.
   463  func makePlatformStringsExpr(ps platformStringsExprs) bf.Expr {
   464  	makeSelect := func(dict *bf.DictExpr) bf.Expr {
   465  		return &bf.CallExpr{
   466  			X:    &bf.LiteralExpr{Token: "select"},
   467  			List: []bf.Expr{dict},
   468  		}
   469  	}
   470  	forceMultiline := func(e bf.Expr) {
   471  		switch e := e.(type) {
   472  		case *bf.ListExpr:
   473  			e.ForceMultiLine = true
   474  		case *bf.CallExpr:
   475  			e.List[0].(*bf.DictExpr).ForceMultiLine = true
   476  		}
   477  	}
   478  
   479  	var parts []bf.Expr
   480  	if ps.generic != nil {
   481  		parts = append(parts, ps.generic)
   482  	}
   483  	if ps.os != nil {
   484  		parts = append(parts, makeSelect(ps.os))
   485  	}
   486  	if ps.arch != nil {
   487  		parts = append(parts, makeSelect(ps.arch))
   488  	}
   489  	if ps.platform != nil {
   490  		parts = append(parts, makeSelect(ps.platform))
   491  	}
   492  
   493  	if len(parts) == 0 {
   494  		return nil
   495  	}
   496  	if len(parts) == 1 {
   497  		return parts[0]
   498  	}
   499  	expr := parts[0]
   500  	forceMultiline(expr)
   501  	for _, part := range parts[1:] {
   502  		forceMultiline(part)
   503  		expr = &bf.BinaryExpr{
   504  			Op: "+",
   505  			X:  expr,
   506  			Y:  part,
   507  		}
   508  	}
   509  	return expr
   510  }
   511  
   512  func mergePlatformStringsExprs(gen, old platformStringsExprs) (platformStringsExprs, error) {
   513  	var ps platformStringsExprs
   514  	var err error
   515  	ps.generic = mergeList(gen.generic, old.generic)
   516  	if ps.os, err = mergeDict(gen.os, old.os); err != nil {
   517  		return platformStringsExprs{}, err
   518  	}
   519  	if ps.arch, err = mergeDict(gen.arch, old.arch); err != nil {
   520  		return platformStringsExprs{}, err
   521  	}
   522  	if ps.platform, err = mergeDict(gen.platform, old.platform); err != nil {
   523  		return platformStringsExprs{}, err
   524  	}
   525  	return ps, nil
   526  }
   527  
   528  func mergeList(gen, old *bf.ListExpr) *bf.ListExpr {
   529  	if old == nil {
   530  		return gen
   531  	}
   532  	if gen == nil {
   533  		gen = &bf.ListExpr{List: []bf.Expr{}}
   534  	}
   535  
   536  	// Build a list of strings from the gen list and keep matching strings
   537  	// in the old list. This preserves comments. Also keep anything with
   538  	// a "# keep" comment, whether or not it's in the gen list.
   539  	genSet := make(map[string]bool)
   540  	for _, v := range gen.List {
   541  		if s := stringValue(v); s != "" {
   542  			genSet[s] = true
   543  		}
   544  	}
   545  
   546  	var merged []bf.Expr
   547  	kept := make(map[string]bool)
   548  	keepComment := false
   549  	for _, v := range old.List {
   550  		s := stringValue(v)
   551  		if keep := shouldKeep(v); keep || genSet[s] {
   552  			keepComment = keepComment || keep
   553  			merged = append(merged, v)
   554  			if s != "" {
   555  				kept[s] = true
   556  			}
   557  		}
   558  	}
   559  
   560  	// Add anything in the gen list that wasn't kept.
   561  	for _, v := range gen.List {
   562  		if s := stringValue(v); kept[s] {
   563  			continue
   564  		}
   565  		merged = append(merged, v)
   566  	}
   567  
   568  	if len(merged) == 0 {
   569  		return nil
   570  	}
   571  	return &bf.ListExpr{
   572  		List:           merged,
   573  		ForceMultiLine: gen.ForceMultiLine || old.ForceMultiLine || keepComment,
   574  	}
   575  }
   576  
   577  func mergeDict(gen, old *bf.DictExpr) (*bf.DictExpr, error) {
   578  	if old == nil {
   579  		return gen, nil
   580  	}
   581  	if gen == nil {
   582  		gen = &bf.DictExpr{List: []bf.Expr{}}
   583  	}
   584  
   585  	var entries []*dictEntry
   586  	entryMap := make(map[string]*dictEntry)
   587  
   588  	for _, kv := range old.List {
   589  		k, v, err := dictEntryKeyValue(kv)
   590  		if err != nil {
   591  			return nil, err
   592  		}
   593  		if _, ok := entryMap[k]; ok {
   594  			return nil, fmt.Errorf("old dict contains more than one case named %q", k)
   595  		}
   596  		e := &dictEntry{key: k, oldValue: v}
   597  		entries = append(entries, e)
   598  		entryMap[k] = e
   599  	}
   600  
   601  	for _, kv := range gen.List {
   602  		k, v, err := dictEntryKeyValue(kv)
   603  		if err != nil {
   604  			return nil, err
   605  		}
   606  		e, ok := entryMap[k]
   607  		if !ok {
   608  			e = &dictEntry{key: k}
   609  			entries = append(entries, e)
   610  			entryMap[k] = e
   611  		}
   612  		e.genValue = v
   613  	}
   614  
   615  	keys := make([]string, 0, len(entries))
   616  	haveDefault := false
   617  	for _, e := range entries {
   618  		e.mergedValue = mergeList(e.genValue, e.oldValue)
   619  		if e.key == "//conditions:default" {
   620  			// Keep the default case, even if it's empty.
   621  			haveDefault = true
   622  			if e.mergedValue == nil {
   623  				e.mergedValue = &bf.ListExpr{}
   624  			}
   625  		} else if e.mergedValue != nil {
   626  			keys = append(keys, e.key)
   627  		}
   628  	}
   629  	if len(keys) == 0 && (!haveDefault || len(entryMap["//conditions:default"].mergedValue.List) == 0) {
   630  		return nil, nil
   631  	}
   632  	sort.Strings(keys)
   633  	// Always put the default case last.
   634  	if haveDefault {
   635  		keys = append(keys, "//conditions:default")
   636  	}
   637  
   638  	mergedEntries := make([]bf.Expr, len(keys))
   639  	for i, k := range keys {
   640  		e := entryMap[k]
   641  		mergedEntries[i] = &bf.KeyValueExpr{
   642  			Key:   &bf.StringExpr{Value: e.key},
   643  			Value: e.mergedValue,
   644  		}
   645  	}
   646  
   647  	return &bf.DictExpr{List: mergedEntries, ForceMultiLine: true}, nil
   648  }
   649  
   650  type dictEntry struct {
   651  	key                             string
   652  	oldValue, genValue, mergedValue *bf.ListExpr
   653  }
   654  
   655  func dictEntryKeyValue(e bf.Expr) (string, *bf.ListExpr, error) {
   656  	kv, ok := e.(*bf.KeyValueExpr)
   657  	if !ok {
   658  		return "", nil, fmt.Errorf("dict entry was not a key-value pair: %#v", e)
   659  	}
   660  	k, ok := kv.Key.(*bf.StringExpr)
   661  	if !ok {
   662  		return "", nil, fmt.Errorf("dict key was not string: %#v", kv.Key)
   663  	}
   664  	v, ok := kv.Value.(*bf.ListExpr)
   665  	if !ok {
   666  		return "", nil, fmt.Errorf("dict value was not list: %#v", kv.Value)
   667  	}
   668  	return k.Value, v, nil
   669  }
   670  
   671  // substituteAttrs contains a list of attributes for each kind that should be
   672  // processed by substituteRule and substituteExpr. Note that "name" does not
   673  // need to be substituted since it's not mergeable.
   674  var substituteAttrs = map[string][]string{
   675  	"go_binary":        {"embed"},
   676  	"go_library":       {"embed"},
   677  	"go_test":          {"embed"},
   678  	"go_proto_library": {"proto"},
   679  }
   680  
   681  // substituteRule replaces local labels (those beginning with ":", referring to
   682  // targets in the same package) according to a substitution map. This is used
   683  // to update generated rules before merging when the corresponding existing
   684  // rules have different names. If substituteRule replaces a string, it returns
   685  // a new expression; it will not modify the original expression.
   686  func substituteRule(call *bf.CallExpr, substitutions map[string]string) *bf.CallExpr {
   687  	rule := bf.Rule{Call: call}
   688  	attrs, ok := substituteAttrs[rule.Kind()]
   689  	if !ok {
   690  		return call
   691  	}
   692  
   693  	didCopy := false
   694  	for i, arg := range call.List {
   695  		kv, ok := arg.(*bf.BinaryExpr)
   696  		if !ok || kv.Op != "=" {
   697  			continue
   698  		}
   699  		key, ok := kv.X.(*bf.LiteralExpr)
   700  		shouldRename := false
   701  		for _, k := range attrs {
   702  			shouldRename = shouldRename || key.Token == k
   703  		}
   704  		if !shouldRename {
   705  			continue
   706  		}
   707  
   708  		value := substituteExpr(kv.Y, substitutions)
   709  		if value != kv.Y {
   710  			if !didCopy {
   711  				didCopy = true
   712  				callCopy := *call
   713  				callCopy.List = append([]bf.Expr{}, call.List...)
   714  				call = &callCopy
   715  			}
   716  			kvCopy := *kv
   717  			kvCopy.Y = value
   718  			call.List[i] = &kvCopy
   719  		}
   720  	}
   721  	return call
   722  }
   723  
   724  // substituteExpr replaces local labels according to a substitution map.
   725  // It only supports string and list expressions (which should be sufficient
   726  // for generated rules). If it replaces a string, it returns a new expression;
   727  // otherwise, it returns e.
   728  func substituteExpr(e bf.Expr, substitutions map[string]string) bf.Expr {
   729  	switch e := e.(type) {
   730  	case *bf.StringExpr:
   731  		if rename, ok := substitutions[strings.TrimPrefix(e.Value, ":")]; ok {
   732  			return &bf.StringExpr{Value: ":" + rename}
   733  		}
   734  	case *bf.ListExpr:
   735  		var listCopy *bf.ListExpr
   736  		for i, elem := range e.List {
   737  			renamed := substituteExpr(elem, substitutions)
   738  			if renamed != elem {
   739  				if listCopy == nil {
   740  					listCopy = new(bf.ListExpr)
   741  					*listCopy = *e
   742  					listCopy.List = append([]bf.Expr{}, e.List...)
   743  				}
   744  				listCopy.List[i] = renamed
   745  			}
   746  		}
   747  		if listCopy != nil {
   748  			return listCopy
   749  		}
   750  	}
   751  	return e
   752  }
   753  
   754  // shouldKeep returns whether an expression from the original file should be
   755  // preserved. This is true if it has a prefix or end-of-line comment "keep".
   756  // Note that bf.Rewrite recognizes "keep sorted" comments which are different,
   757  // so we don't recognize comments that only start with "keep".
   758  func shouldKeep(e bf.Expr) bool {
   759  	for _, c := range append(e.Comment().Before, e.Comment().Suffix...) {
   760  		text := strings.TrimSpace(strings.TrimPrefix(c.Token, "#"))
   761  		if text == keep {
   762  			return true
   763  		}
   764  	}
   765  	return false
   766  }
   767  
   768  // matchAttrs contains lists of attributes for each kind that are used in
   769  // matching. For example, importpath attributes can be used to match go_library
   770  // rules, even when the names are different.
   771  var matchAttrs = map[string][]string{
   772  	"go_library":       {"importpath"},
   773  	"go_proto_library": {"importpath"},
   774  	"go_repository":    {"importpath"},
   775  }
   776  
   777  // matchAny is a set of kinds which may be matched regardless of attributes.
   778  // For example, if there is only one go_binary in a package, any go_binary
   779  // rule will match.
   780  var matchAny = map[string]bool{"go_binary": true}
   781  
   782  // match searches for a rule that can be merged with x in stmts.
   783  //
   784  // A rule is considered a match if its kind is equal to x's kind AND either its
   785  // name is equal OR at least one of the attributes in matchAttrs is equal.
   786  //
   787  // If there are no matches, nil, -1, and nil are returned.
   788  //
   789  // If a rule has the same name but a different kind, nil, -1, and an error
   790  // are returned.
   791  //
   792  // If there is exactly one match, the rule, its index in stmts, and nil
   793  // are returned.
   794  //
   795  // If there are multiple matches, match will attempt to disambiguate, based on
   796  // the quality of the match (name match is best, then attribute match in the
   797  // order that attributes are listed). If disambiguation is successful,
   798  // the rule, its index in stmts, and nil are returned. Otherwise, nil, -1,
   799  // and an error are returned.
   800  func match(stmts []bf.Expr, x *bf.CallExpr) (*bf.CallExpr, int, error) {
   801  	type matchInfo struct {
   802  		rule  bf.Rule
   803  		index int
   804  	}
   805  
   806  	xr := bf.Rule{Call: x}
   807  	xname := xr.Name()
   808  	xkind := xr.Kind()
   809  	var nameMatches []matchInfo
   810  	var kindMatches []matchInfo
   811  	for i, s := range stmts {
   812  		y, ok := s.(*bf.CallExpr)
   813  		if !ok {
   814  			continue
   815  		}
   816  		yr := bf.Rule{Call: y}
   817  		if xname == yr.Name() {
   818  			nameMatches = append(nameMatches, matchInfo{yr, i})
   819  		}
   820  		if xkind == yr.Kind() {
   821  			kindMatches = append(kindMatches, matchInfo{yr, i})
   822  		}
   823  	}
   824  
   825  	if len(nameMatches) == 1 {
   826  		if ykind := nameMatches[0].rule.Kind(); xkind != ykind {
   827  			return nil, -1, fmt.Errorf("could not merge %s(%s): a rule of the same name has kind %s", xkind, xname, ykind)
   828  		}
   829  		return nameMatches[0].rule.Call, nameMatches[0].index, nil
   830  	}
   831  	if len(nameMatches) > 1 {
   832  		return nil, -1, fmt.Errorf("could not merge %s(%s): multiple rules have the same name", xname)
   833  	}
   834  
   835  	attrs := matchAttrs[xr.Kind()]
   836  	for _, key := range attrs {
   837  		var attrMatches []matchInfo
   838  		xvalue := xr.AttrString(key)
   839  		if xvalue == "" {
   840  			continue
   841  		}
   842  		for _, m := range kindMatches {
   843  			if xvalue == m.rule.AttrString(key) {
   844  				attrMatches = append(attrMatches, m)
   845  			}
   846  		}
   847  		if len(attrMatches) == 1 {
   848  			return attrMatches[0].rule.Call, attrMatches[0].index, nil
   849  		} else if len(attrMatches) > 1 {
   850  			return nil, -1, fmt.Errorf("could not merge %s(%s): multiple rules have the same attribute %s = %q", xkind, xname, key, xvalue)
   851  		}
   852  	}
   853  
   854  	if matchAny[xkind] {
   855  		if len(kindMatches) == 1 {
   856  			return kindMatches[0].rule.Call, kindMatches[0].index, nil
   857  		} else if len(kindMatches) > 1 {
   858  			return nil, -1, fmt.Errorf("could not merge %s(%s): multiple rules have the same kind but different names", xkind, xname)
   859  		}
   860  	}
   861  
   862  	return nil, -1, nil
   863  }
   864  
   865  func kind(c *bf.CallExpr) string {
   866  	return (&bf.Rule{Call: c}).Kind()
   867  }
   868  
   869  func name(c *bf.CallExpr) string {
   870  	return (&bf.Rule{Call: c}).Name()
   871  }
   872  
   873  // isRuleEmpty returns true if a rule cannot be built because it has no sources,
   874  // dependencies, or embeds after merging. This is based on a per-kind whitelist
   875  // of attributes. Other attributes, like "name" and "visibility" don't affect
   876  // emptiness. Always returns false for expressions that aren't in the known
   877  // set of rules.
   878  func isRuleEmpty(e bf.Expr) bool {
   879  	c, ok := e.(*bf.CallExpr)
   880  	if !ok {
   881  		return false
   882  	}
   883  	r := bf.Rule{Call: c}
   884  	kind := r.Kind()
   885  	if nonEmptyAttrs[kind] == nil {
   886  		return false
   887  	}
   888  	for _, attr := range r.AttrKeys() {
   889  		if nonEmptyAttrs[kind][attr] {
   890  			return false
   891  		}
   892  	}
   893  	return true
   894  }
   895  
   896  func isScalar(e bf.Expr) bool {
   897  	switch e.(type) {
   898  	case *bf.StringExpr, *bf.LiteralExpr:
   899  		return true
   900  	default:
   901  		return false
   902  	}
   903  }
   904  
   905  func stringValue(e bf.Expr) string {
   906  	s, ok := e.(*bf.StringExpr)
   907  	if !ok {
   908  		return ""
   909  	}
   910  	return s.Value
   911  }
   912  
   913  // deleteIndices copies a list, dropping elements at deletedIndices.
   914  // deletedIndices must be sorted.
   915  func deleteIndices(stmt []bf.Expr, deletedIndices []int) []bf.Expr {
   916  	kept := make([]bf.Expr, 0, len(stmt)-len(deletedIndices))
   917  	di := 0
   918  	for i, s := range stmt {
   919  		if di < len(deletedIndices) && i == deletedIndices[di] {
   920  			di++
   921  			continue
   922  		}
   923  		kept = append(kept, s)
   924  	}
   925  	return kept
   926  }