github.com/bazelbuild/bazel-gazelle@v0.36.1-0.20240520142334-61b277ba6fed/rule/rule.go (about)

     1  /* Copyright 2018 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 rule provides tools for editing Bazel build files. It is intended to
    17  // be a more powerful replacement for
    18  // github.com/bazelbuild/buildtools/build.Rule, adapted for Gazelle's usage. It
    19  // is language agnostic, but it may be used for language-specific rules by
    20  // providing configuration.
    21  //
    22  // File is the primary interface to this package. A File represents an
    23  // individual build file. It comprises a list of Rules and a list of Loads.
    24  // Rules and Loads may be inserted, modified, or deleted. When all changes
    25  // are done, File.Save() may be called to write changes back to a file.
    26  package rule
    27  
    28  import (
    29  	"fmt"
    30  	"io/fs"
    31  	"os"
    32  	"path/filepath"
    33  	"sort"
    34  	"strings"
    35  
    36  	bzl "github.com/bazelbuild/buildtools/build"
    37  	bt "github.com/bazelbuild/buildtools/tables"
    38  )
    39  
    40  // File provides editing functionality for a build file. You can create a
    41  // new file with EmptyFile or load an existing file with LoadFile. After
    42  // changes have been made, call Save to write changes back to a file.
    43  type File struct {
    44  	// File is the underlying build file syntax tree. Some editing operations
    45  	// may modify this, but editing is not complete until Sync() is called.
    46  	File *bzl.File
    47  
    48  	// function is the underlying syntax tree of a bzl file function.
    49  	// This is used for editing the bzl file function specified by the
    50  	// update-repos -to_macro option.
    51  	function *function
    52  
    53  	// Pkg is the Bazel package this build file defines.
    54  	Pkg string
    55  
    56  	// Path is the file system path to the build file (same as File.Path).
    57  	Path string
    58  
    59  	// DefName is the name of the function definition this File refers to
    60  	// if loaded with LoadMacroFile or a similar function. Normally empty.
    61  	DefName string
    62  
    63  	// Directives is a list of configuration directives found in top-level
    64  	// comments in the file. This should not be modified after the file is read.
    65  	Directives []Directive
    66  
    67  	// Loads is a list of load statements within the file. This should not
    68  	// be modified directly; use Load methods instead.
    69  	Loads []*Load
    70  
    71  	// Rules is a list of rules within the file (or function calls that look like
    72  	// rules). This should not be modified directly; use Rule methods instead.
    73  	Rules []*Rule
    74  
    75  	// Content is the file's underlying disk content, which is recorded when the
    76  	// file is initially loaded and whenever it is saved back to disk. If the file
    77  	// is modified outside of Rule methods, Content must be manually updated in
    78  	// order to keep it in sync.
    79  	Content []byte
    80  }
    81  
    82  // EmptyFile creates a File wrapped around an empty syntax tree.
    83  func EmptyFile(path, pkg string) *File {
    84  	return &File{
    85  		File: &bzl.File{Path: path, Type: bzl.TypeBuild},
    86  		Path: path,
    87  		Pkg:  pkg,
    88  	}
    89  }
    90  
    91  // LoadFile loads a build file from disk, parses it, and scans for rules and
    92  // load statements. The syntax tree within the returned File will be modified
    93  // by editing methods.
    94  //
    95  // This function returns I/O and parse errors without modification. It's safe
    96  // to use os.IsNotExist and similar predicates.
    97  func LoadFile(path, pkg string) (*File, error) {
    98  	data, err := os.ReadFile(path)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	return LoadData(path, pkg, data)
   103  }
   104  
   105  // LoadWorkspaceFile is similar to LoadFile but parses the file as a WORKSPACE
   106  // file.
   107  func LoadWorkspaceFile(path, pkg string) (*File, error) {
   108  	data, err := os.ReadFile(path)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	return LoadWorkspaceData(path, pkg, data)
   113  }
   114  
   115  // LoadMacroFile loads a bzl file from disk, parses it, then scans for the load
   116  // statements and the rules called from the given Starlark function. If there is
   117  // no matching function name, then a new function with that name will be created.
   118  // The function's syntax tree will be returned within File and can be modified by
   119  // Sync and Save calls.
   120  func LoadMacroFile(path, pkg, defName string) (*File, error) {
   121  	data, err := os.ReadFile(path)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	return LoadMacroData(path, pkg, defName, data)
   126  }
   127  
   128  // EmptyMacroFile creates a bzl file at the given path and within the file creates
   129  // a Starlark function with the provided name. The function can then be modified
   130  // by Sync and Save calls.
   131  func EmptyMacroFile(path, pkg, defName string) (*File, error) {
   132  	_, err := os.Create(path)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	return LoadMacroData(path, pkg, defName, nil)
   137  }
   138  
   139  // LoadData parses a build file from a byte slice and scans it for rules and
   140  // load statements. The syntax tree within the returned File will be modified
   141  // by editing methods.
   142  func LoadData(path, pkg string, data []byte) (*File, error) {
   143  	ast, err := bzl.ParseBuild(path, data)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	f := ScanAST(pkg, ast)
   148  	if err := checkFile(f); err != nil {
   149  		return nil, err
   150  	}
   151  	f.Content = data
   152  	return f, nil
   153  }
   154  
   155  // LoadWorkspaceData is similar to LoadData but parses the data as a
   156  // WORKSPACE file.
   157  func LoadWorkspaceData(path, pkg string, data []byte) (*File, error) {
   158  	ast, err := bzl.ParseWorkspace(path, data)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	f := ScanAST(pkg, ast)
   163  	if err := checkFile(f); err != nil {
   164  		return nil, err
   165  	}
   166  	f.Content = data
   167  	return f, nil
   168  }
   169  
   170  // LoadMacroData parses a bzl file from a byte slice and scans for the load
   171  // statements and the rules called from the given Starlark function. If there is
   172  // no matching function name, then a new function will be created, and added to the
   173  // File the next time Sync is called. The function's syntax tree will be returned
   174  // within File and can be modified by Sync and Save calls.
   175  func LoadMacroData(path, pkg, defName string, data []byte) (*File, error) {
   176  	ast, err := bzl.ParseBzl(path, data)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  	f := ScanASTBody(pkg, defName, ast)
   181  	if err := checkFile(f); err != nil {
   182  		return nil, err
   183  	}
   184  	f.Content = data
   185  	return f, nil
   186  }
   187  
   188  // ScanAST creates a File wrapped around the given syntax tree. This tree
   189  // will be modified by editing methods.
   190  func ScanAST(pkg string, bzlFile *bzl.File) *File {
   191  	return ScanASTBody(pkg, "", bzlFile)
   192  }
   193  
   194  type function struct {
   195  	stmt              *bzl.DefStmt
   196  	inserted, hasPass bool
   197  }
   198  
   199  // ScanASTBody creates a File wrapped around the given syntax tree. It will also
   200  // scan the AST for a function matching the given defName, and if the function
   201  // does not exist it will create a new one and mark it to be added to the File
   202  // the next time Sync is called.
   203  func ScanASTBody(pkg, defName string, bzlFile *bzl.File) *File {
   204  	f := &File{
   205  		File:    bzlFile,
   206  		Pkg:     pkg,
   207  		Path:    bzlFile.Path,
   208  		DefName: defName,
   209  	}
   210  	var defStmt *bzl.DefStmt
   211  	f.Rules, f.Loads, defStmt = scanExprs(defName, bzlFile.Stmt)
   212  	if defStmt != nil {
   213  		f.Rules, _, _ = scanExprs("", defStmt.Body)
   214  		f.function = &function{
   215  			stmt:     defStmt,
   216  			inserted: true,
   217  		}
   218  		if len(defStmt.Body) == 1 {
   219  			if v, ok := defStmt.Body[0].(*bzl.BranchStmt); ok && v.Token == "pass" {
   220  				f.function.hasPass = true
   221  			}
   222  		}
   223  	} else if defName != "" {
   224  		f.function = &function{
   225  			stmt:     &bzl.DefStmt{Name: defName},
   226  			inserted: false,
   227  		}
   228  	}
   229  	if f.function != nil {
   230  		f.Directives = ParseDirectivesFromMacro(f.function.stmt)
   231  	} else {
   232  		f.Directives = ParseDirectives(bzlFile)
   233  	}
   234  	return f
   235  }
   236  
   237  func scanExprs(defName string, stmt []bzl.Expr) (rules []*Rule, loads []*Load, fn *bzl.DefStmt) {
   238  	for i, expr := range stmt {
   239  		switch expr := expr.(type) {
   240  		case *bzl.LoadStmt:
   241  			l := loadFromExpr(i, expr)
   242  			loads = append(loads, l)
   243  		case *bzl.CallExpr:
   244  			if r := ruleFromExpr(i, expr); r != nil {
   245  				rules = append(rules, r)
   246  			}
   247  		case *bzl.DefStmt:
   248  			if expr.Name == defName {
   249  				fn = expr
   250  			}
   251  		}
   252  	}
   253  	return rules, loads, fn
   254  }
   255  
   256  // MatchBuildFile looks for a file in files that has a name from names.
   257  // If there is at least one matching file, a path will be returned by joining
   258  // dir and the first matching name. If there are no matching files, the
   259  // empty string is returned.
   260  func MatchBuildFile(dir string, names []string, ents []fs.DirEntry) string {
   261  	for _, name := range names {
   262  		for _, ent := range ents {
   263  			if ent.Name() == name && !ent.IsDir() {
   264  				return filepath.Join(dir, name)
   265  			}
   266  		}
   267  	}
   268  	return ""
   269  }
   270  
   271  // Deprecated: Prefer MatchBuildFile, it's more efficient to fetch a []fs.DirEntry
   272  func MatchBuildFileName(dir string, names []string, files []os.FileInfo) string {
   273  	for _, name := range names {
   274  		for _, fi := range files {
   275  			if fi.Name() == name && !fi.IsDir() {
   276  				return filepath.Join(dir, name)
   277  			}
   278  		}
   279  	}
   280  	return ""
   281  }
   282  
   283  // SyncMacroFile syncs the file's syntax tree with another file's. This is
   284  // useful for keeping multiple macro definitions from the same .bzl file in sync.
   285  func (f *File) SyncMacroFile(from *File) {
   286  	fromFunc := *from.function.stmt
   287  	_, _, toFunc := scanExprs(from.function.stmt.Name, f.File.Stmt)
   288  	if toFunc != nil {
   289  		*toFunc = fromFunc
   290  	} else {
   291  		f.File.Stmt = append(f.File.Stmt, &fromFunc)
   292  	}
   293  }
   294  
   295  // MacroName returns the name of the macro function that this file is editing,
   296  // or an empty string if a macro function is not being edited.
   297  func (f *File) MacroName() string {
   298  	if f.function != nil && f.function.stmt != nil {
   299  		return f.function.stmt.Name
   300  	}
   301  	return ""
   302  }
   303  
   304  // Sync writes all changes back to the wrapped syntax tree. This should be
   305  // called after editing operations, before reading the syntax tree again.
   306  func (f *File) Sync() {
   307  	var loadInserts, loadDeletes, loadStmts []*stmt
   308  	var r, w int
   309  	for r, w = 0, 0; r < len(f.Loads); r++ {
   310  		s := f.Loads[r]
   311  		s.sync()
   312  		if s.deleted {
   313  			loadDeletes = append(loadDeletes, &s.stmt)
   314  			continue
   315  		}
   316  		if s.inserted {
   317  			loadInserts = append(loadInserts, &s.stmt)
   318  			s.inserted = false
   319  		} else {
   320  			loadStmts = append(loadStmts, &s.stmt)
   321  		}
   322  		f.Loads[w] = s
   323  		w++
   324  	}
   325  	f.Loads = f.Loads[:w]
   326  	var ruleInserts, ruleDeletes, ruleStmts []*stmt
   327  	for r, w = 0, 0; r < len(f.Rules); r++ {
   328  		s := f.Rules[r]
   329  		s.sync()
   330  		if s.deleted {
   331  			ruleDeletes = append(ruleDeletes, &s.stmt)
   332  			continue
   333  		}
   334  		if s.inserted {
   335  			ruleInserts = append(ruleInserts, &s.stmt)
   336  			s.inserted = false
   337  		} else {
   338  			ruleStmts = append(ruleStmts, &s.stmt)
   339  		}
   340  		f.Rules[w] = s
   341  		w++
   342  	}
   343  	f.Rules = f.Rules[:w]
   344  
   345  	if f.function == nil {
   346  		deletes := append(loadDeletes, ruleDeletes...)
   347  		inserts := append(loadInserts, ruleInserts...)
   348  		stmts := append(loadStmts, ruleStmts...)
   349  		updateStmt(&f.File.Stmt, inserts, deletes, stmts)
   350  	} else {
   351  		updateStmt(&f.File.Stmt, loadInserts, loadDeletes, loadStmts)
   352  		if f.function.hasPass && len(ruleInserts) > 0 {
   353  			f.function.stmt.Body = []bzl.Expr{}
   354  			f.function.hasPass = false
   355  		}
   356  		updateStmt(&f.function.stmt.Body, ruleInserts, ruleDeletes, ruleStmts)
   357  		if len(f.function.stmt.Body) == 0 {
   358  			f.function.stmt.Body = append(f.function.stmt.Body, &bzl.BranchStmt{Token: "pass"})
   359  			f.function.hasPass = true
   360  		}
   361  		if !f.function.inserted {
   362  			f.File.Stmt = append(f.File.Stmt, f.function.stmt)
   363  			f.function.inserted = true
   364  		}
   365  	}
   366  }
   367  
   368  func updateStmt(oldStmt *[]bzl.Expr, inserts, deletes, stmts []*stmt) {
   369  	sort.Stable(byIndex(deletes))
   370  	sort.Stable(byIndex(inserts))
   371  	sort.Stable(byIndex(stmts))
   372  	cap := len(*oldStmt) - len(deletes) + len(inserts)
   373  	if cap < 0 {
   374  		cap = 0
   375  	}
   376  	newStmt := make([]bzl.Expr, 0, cap)
   377  	var ii, di, si int
   378  	for i, stmt := range *oldStmt {
   379  		for ii < len(inserts) && inserts[ii].index == i {
   380  			inserts[ii].index = len(newStmt)
   381  			newStmt = append(newStmt, inserts[ii].expr)
   382  			ii++
   383  		}
   384  		if di < len(deletes) && deletes[di].index == i {
   385  			di++
   386  			continue
   387  		}
   388  		if si < len(stmts) && stmts[si].expr == stmt {
   389  			stmts[si].index = len(newStmt)
   390  			si++
   391  		}
   392  		newStmt = append(newStmt, stmt)
   393  	}
   394  	for ii < len(inserts) {
   395  		inserts[ii].index = len(newStmt)
   396  		newStmt = append(newStmt, inserts[ii].expr)
   397  		ii++
   398  	}
   399  	*oldStmt = newStmt
   400  }
   401  
   402  // Format formats the build file in a form that can be written to disk.
   403  // This method calls Sync internally.
   404  func (f *File) Format() []byte {
   405  	f.Sync()
   406  	return bzl.Format(f.File)
   407  }
   408  
   409  // SortMacro sorts rules and loads in the macro of this File. It doesn't sort the rules if
   410  // this File does not have a macro, e.g., WORKSPACE.
   411  // This method calls Sync internally.
   412  func (f *File) SortMacro() {
   413  	f.Sync()
   414  
   415  	if f.function == nil {
   416  		panic(fmt.Sprintf("%s: not loaded as macro file", f.Path))
   417  	}
   418  
   419  	sort.Stable(loadsByName{f.Loads, f.File.Stmt})
   420  	sort.Stable(rulesByKindAndName{f.Rules, f.function.stmt.Body})
   421  }
   422  
   423  // Save writes the build file to disk. This method calls Sync internally.
   424  func (f *File) Save(path string) error {
   425  	f.Sync()
   426  	f.Content = bzl.Format(f.File)
   427  	return os.WriteFile(path, f.Content, 0o666)
   428  }
   429  
   430  // HasDefaultVisibility returns whether the File contains a "package" rule with
   431  // a "default_visibility" attribute. Rules generated by Gazelle should not
   432  // have their own visibility attributes if this is the case.
   433  func (f *File) HasDefaultVisibility() bool {
   434  	for _, r := range f.Rules {
   435  		if r.Kind() == "package" && r.Attr("default_visibility") != nil {
   436  			return true
   437  		}
   438  	}
   439  	return false
   440  }
   441  
   442  type stmt struct {
   443  	index                      int
   444  	deleted, inserted, updated bool
   445  	comments                   []string
   446  	commentsUpdated            bool
   447  	expr                       bzl.Expr
   448  }
   449  
   450  // Index returns the index for this statement within the build file. For
   451  // inserted rules, this is where the rule will be inserted (rules with the
   452  // same index will be inserted in the order Insert was called). For existing
   453  // rules, this is the index of the original statement.
   454  func (s *stmt) Index() int { return s.index }
   455  
   456  // Delete marks this statement for deletion. It will be removed from the
   457  // syntax tree when File.Sync is called.
   458  func (s *stmt) Delete() { s.deleted = true }
   459  
   460  // Comments returns the text of the comments that appear before the statement.
   461  // Each comment includes the leading "#".
   462  func (s *stmt) Comments() []string {
   463  	return s.comments
   464  }
   465  
   466  // AddComment adds a new comment above the statement, after other comments.
   467  // The new comment must start with "#".
   468  func (s *stmt) AddComment(token string) {
   469  	if !strings.HasPrefix(token, "#") {
   470  		panic(fmt.Sprintf("comment must start with '#': got %q", token))
   471  	}
   472  	s.comments = append(s.comments, token)
   473  	s.commentsUpdated = true
   474  }
   475  
   476  func commentsFromExpr(e bzl.Expr) []string {
   477  	before := e.Comment().Before
   478  	tokens := make([]string, len(before))
   479  	for i, c := range before {
   480  		tokens[i] = c.Token
   481  	}
   482  	return tokens
   483  }
   484  
   485  func (s *stmt) syncComments() {
   486  	if !s.commentsUpdated {
   487  		return
   488  	}
   489  	s.commentsUpdated = false
   490  	before := make([]bzl.Comment, len(s.comments))
   491  	for i, token := range s.comments {
   492  		before[i].Token = token
   493  	}
   494  	s.expr.Comment().Before = before
   495  }
   496  
   497  type byIndex []*stmt
   498  
   499  func (s byIndex) Len() int {
   500  	return len(s)
   501  }
   502  
   503  func (s byIndex) Less(i, j int) bool {
   504  	return s[i].index < s[j].index
   505  }
   506  
   507  func (s byIndex) Swap(i, j int) {
   508  	s[i], s[j] = s[j], s[i]
   509  }
   510  
   511  type rulesByKindAndName struct {
   512  	rules []*Rule
   513  	exprs []bzl.Expr
   514  }
   515  
   516  // type checking
   517  var _ sort.Interface = rulesByKindAndName{}
   518  
   519  func (s rulesByKindAndName) Len() int {
   520  	return len(s.rules)
   521  }
   522  
   523  func (s rulesByKindAndName) Less(i, j int) bool {
   524  	if s.rules[i].Kind() == s.rules[j].Kind() {
   525  		return s.rules[i].Name() < s.rules[j].Name()
   526  	}
   527  	return s.rules[i].Kind() < s.rules[j].Kind()
   528  }
   529  
   530  func (s rulesByKindAndName) Swap(i, j int) {
   531  	s.exprs[s.rules[i].index], s.exprs[s.rules[j].index] = s.exprs[s.rules[j].index], s.exprs[s.rules[i].index]
   532  	s.rules[i].index, s.rules[j].index = s.rules[j].index, s.rules[i].index
   533  	s.rules[i], s.rules[j] = s.rules[j], s.rules[i]
   534  }
   535  
   536  type loadsByName struct {
   537  	loads []*Load
   538  	exprs []bzl.Expr
   539  }
   540  
   541  // type checking
   542  var _ sort.Interface = loadsByName{}
   543  
   544  func (s loadsByName) Len() int {
   545  	return len(s.loads)
   546  }
   547  
   548  func (s loadsByName) Less(i, j int) bool {
   549  	return s.loads[i].Name() < s.loads[j].Name()
   550  }
   551  
   552  func (s loadsByName) Swap(i, j int) {
   553  	s.exprs[s.loads[i].index], s.exprs[s.loads[j].index] = s.exprs[s.loads[j].index], s.exprs[s.loads[i].index]
   554  	s.loads[i].index, s.loads[j].index = s.loads[j].index, s.loads[i].index
   555  	s.loads[i], s.loads[j] = s.loads[j], s.loads[i]
   556  }
   557  
   558  // identPair represents one symbol, with or without remapping, in a load
   559  // statement within a build file.
   560  type identPair struct {
   561  	to, from *bzl.Ident
   562  }
   563  
   564  // Load represents a load statement within a build file.
   565  type Load struct {
   566  	stmt
   567  	name    string
   568  	symbols map[string]identPair
   569  }
   570  
   571  // NewLoad creates a new, empty load statement for the given file name.
   572  func NewLoad(name string) *Load {
   573  	return &Load{
   574  		stmt: stmt{
   575  			expr: &bzl.LoadStmt{
   576  				Module:       &bzl.StringExpr{Value: name},
   577  				ForceCompact: true,
   578  			},
   579  		},
   580  		name:    name,
   581  		symbols: make(map[string]identPair),
   582  	}
   583  }
   584  
   585  func loadFromExpr(index int, loadStmt *bzl.LoadStmt) *Load {
   586  	l := &Load{
   587  		stmt: stmt{
   588  			index:    index,
   589  			expr:     loadStmt,
   590  			comments: commentsFromExpr(loadStmt),
   591  		},
   592  		name:    loadStmt.Module.Value,
   593  		symbols: make(map[string]identPair),
   594  	}
   595  	for i := range loadStmt.From {
   596  		to, from := loadStmt.To[i], loadStmt.From[i]
   597  		l.symbols[to.Name] = identPair{to: to, from: from}
   598  	}
   599  	return l
   600  }
   601  
   602  // Name returns the name of the file this statement loads.
   603  func (l *Load) Name() string {
   604  	return l.name
   605  }
   606  
   607  // Symbols returns a sorted list of symbols this statement loads.
   608  // If the symbol is loaded with a name different from its definition, the
   609  // loaded name is returned, not the original name.
   610  func (l *Load) Symbols() []string {
   611  	syms := make([]string, 0, len(l.symbols))
   612  	for sym := range l.symbols {
   613  		syms = append(syms, sym)
   614  	}
   615  	sort.Strings(syms)
   616  	return syms
   617  }
   618  
   619  // SymbolPairs returns a list of symbol pairs loaded by this statement.
   620  // Each pair contains the symbol defined in the loaded module (From) and the
   621  // symbol declared in the loading module (To). The pairs are sorted by To
   622  // (same order as Symbols).
   623  func (l *Load) SymbolPairs() []struct{ From, To string } {
   624  	toSyms := l.Symbols()
   625  	pairs := make([]struct{ From, To string }, 0, len(toSyms))
   626  	for _, toSym := range toSyms {
   627  		pairs = append(pairs, struct{ From, To string }{l.symbols[toSym].from.Name, toSym})
   628  	}
   629  	return pairs
   630  }
   631  
   632  // Has returns true if sym is loaded by this statement.
   633  func (l *Load) Has(sym string) bool {
   634  	_, ok := l.symbols[sym]
   635  	return ok
   636  }
   637  
   638  // Unalias returns the original (from) name of a (to) Symbol from a load.
   639  func (l *Load) Unalias(sym string) string {
   640  	return l.symbols[sym].from.Name
   641  }
   642  
   643  // Add inserts a new symbol into the load statement. This has no effect if
   644  // the symbol is already loaded. Symbols will be sorted, so the order
   645  // doesn't matter.
   646  func (l *Load) Add(sym string) {
   647  	if _, ok := l.symbols[sym]; !ok {
   648  		i := &bzl.Ident{Name: sym}
   649  		l.symbols[sym] = identPair{to: i, from: i}
   650  		l.updated = true
   651  	}
   652  }
   653  
   654  // AddAlias inserts a new aliased symbol into the load statement. This has
   655  // no effect if the symbol is already loaded. Symbols will be sorted, so the order
   656  // doesn't matter.
   657  func (l *Load) AddAlias(sym, to string) {
   658  	if _, ok := l.symbols[sym]; !ok {
   659  		l.symbols[sym] = identPair{
   660  			to:   &bzl.Ident{Name: to},
   661  			from: &bzl.Ident{Name: sym},
   662  		}
   663  		l.updated = true
   664  	}
   665  }
   666  
   667  // Remove deletes a symbol from the load statement. This has no effect if
   668  // the symbol is not loaded.
   669  func (l *Load) Remove(sym string) {
   670  	if _, ok := l.symbols[sym]; ok {
   671  		delete(l.symbols, sym)
   672  		l.updated = true
   673  	}
   674  }
   675  
   676  // IsEmpty returns whether this statement loads any symbols.
   677  func (l *Load) IsEmpty() bool {
   678  	return len(l.symbols) == 0
   679  }
   680  
   681  // Insert marks this statement for insertion at the given index. If multiple
   682  // statements are inserted at the same index, they will be inserted in the
   683  // order Insert is called.
   684  func (l *Load) Insert(f *File, index int) {
   685  	l.index = index
   686  	l.inserted = true
   687  	f.Loads = append(f.Loads, l)
   688  }
   689  
   690  func (l *Load) sync() {
   691  	l.syncComments()
   692  	if !l.updated {
   693  		return
   694  	}
   695  	l.updated = false
   696  
   697  	// args1 and args2 are two different sort groups based on whether a remap of the identifier is present.
   698  	var args1, args2, args []string
   699  	for sym, pair := range l.symbols {
   700  		if pair.from.Name == pair.to.Name {
   701  			args1 = append(args1, sym)
   702  		} else {
   703  			args2 = append(args2, sym)
   704  		}
   705  	}
   706  	sort.Strings(args1)
   707  	sort.Strings(args2)
   708  	args = append(args, args1...)
   709  	args = append(args, args2...)
   710  
   711  	loadStmt := l.expr.(*bzl.LoadStmt)
   712  	loadStmt.Module.Value = l.name
   713  	loadStmt.From = make([]*bzl.Ident, 0, len(args))
   714  	loadStmt.To = make([]*bzl.Ident, 0, len(args))
   715  	for _, sym := range args {
   716  		pair := l.symbols[sym]
   717  		loadStmt.From = append(loadStmt.From, pair.from)
   718  		loadStmt.To = append(loadStmt.To, pair.to)
   719  		if pair.from.Name != pair.to.Name {
   720  			loadStmt.ForceCompact = false
   721  		}
   722  	}
   723  }
   724  
   725  // Rule represents a rule statement within a build file.
   726  type Rule struct {
   727  	stmt
   728  	kind        bzl.Expr
   729  	args        []bzl.Expr
   730  	attrs       map[string]attrValue
   731  	private     map[string]interface{}
   732  	sortedAttrs []string
   733  }
   734  
   735  type attrValue struct {
   736  	// expr is the expression that defines the attribute assignment. If mergeable
   737  	// this will be replaced with a call to the merge function.
   738  	expr *bzl.AssignExpr
   739  	// val is the value of the attribute. If the attribute is mergeable
   740  	// the value must implement the Merger interface. could be nil.
   741  	val interface{}
   742  }
   743  
   744  // NewRule creates a new, empty rule with the given kind and name.
   745  func NewRule(kind, name string) *Rule {
   746  	kindIdent := createDotExpr(kind)
   747  	call := &bzl.CallExpr{X: kindIdent}
   748  
   749  	r := &Rule{
   750  		stmt:        stmt{expr: call},
   751  		kind:        kindIdent,
   752  		attrs:       map[string]attrValue{},
   753  		private:     map[string]interface{}{},
   754  		sortedAttrs: []string{"deps", "srcs"},
   755  	}
   756  	if name != "" {
   757  		nameAttr := attrValue{
   758  			expr: &bzl.AssignExpr{
   759  				LHS: &bzl.Ident{Name: "name"},
   760  				RHS: &bzl.StringExpr{Value: name},
   761  				Op:  "=",
   762  			},
   763  			val: name}
   764  		call.List = []bzl.Expr{nameAttr.expr}
   765  		r.attrs["name"] = nameAttr
   766  	}
   767  	return r
   768  }
   769  
   770  // Creates `bzl.Expr` for a kind which
   771  // is either `*bzl.DotExpr` or `*bzl.Ident`.
   772  //
   773  // For `myKind` kind it returns:
   774  //
   775  //	&bzl.Ident{
   776  //	    Name: "myKind"
   777  //	}
   778  //
   779  // For `myKind.inner` kind it returns:
   780  //
   781  //	&bzl.DotExpr{
   782  //	    Name: "inner",
   783  //	    X: &bzl.Ident {
   784  //	        Name: "myKind"
   785  //	    }
   786  //	}
   787  func createDotExpr(kind string) bzl.Expr {
   788  	var expr bzl.Expr
   789  	parts := strings.Split(kind, ".")
   790  
   791  	if len(parts) > 1 {
   792  		// last `parts` element is the main bzl.DotExpr
   793  		var dotExpr *bzl.DotExpr = &bzl.DotExpr{Name: parts[len(parts)-1]}
   794  
   795  		_pDot := dotExpr
   796  
   797  		for idx := len(parts) - 2; idx > 0; idx-- {
   798  			d := &bzl.DotExpr{Name: parts[idx]}
   799  			_pDot.X = d
   800  			_pDot = d
   801  		}
   802  
   803  		// first `parts` element is the identifier
   804  		_pDot.X = &bzl.Ident{Name: parts[0]}
   805  		expr = dotExpr
   806  	} else {
   807  		expr = &bzl.Ident{Name: kind}
   808  	}
   809  
   810  	return expr
   811  }
   812  
   813  func isNestedDotOrIdent(expr bzl.Expr) bool {
   814  	if _, ok := expr.(*bzl.Ident); ok {
   815  		return true
   816  	}
   817  
   818  	dot, ok := expr.(*bzl.DotExpr)
   819  	if !ok {
   820  		return false
   821  	}
   822  
   823  	return isNestedDotOrIdent(dot.X)
   824  }
   825  
   826  func ruleFromExpr(index int, expr bzl.Expr) *Rule {
   827  	call, ok := expr.(*bzl.CallExpr)
   828  	if !ok {
   829  		return nil
   830  	}
   831  
   832  	kind := call.X
   833  	if !isNestedDotOrIdent(kind) {
   834  		return nil
   835  	}
   836  
   837  	var args []bzl.Expr
   838  	attrs := make(map[string]attrValue, len(call.List))
   839  	for _, arg := range call.List {
   840  		if attr, ok := arg.(*bzl.AssignExpr); ok {
   841  			key := attr.LHS.(*bzl.Ident) // required by parser
   842  			attrs[key.Name] = attrValue{expr: attr}
   843  		} else {
   844  			args = append(args, arg)
   845  		}
   846  	}
   847  	return &Rule{
   848  		stmt: stmt{
   849  			index:    index,
   850  			expr:     call,
   851  			comments: commentsFromExpr(expr),
   852  		},
   853  		kind:    kind,
   854  		args:    args,
   855  		attrs:   attrs,
   856  		private: map[string]interface{}{},
   857  	}
   858  }
   859  
   860  // ShouldKeep returns whether the rule is marked with a "# keep" comment. Rules
   861  // that are kept should not be modified. This does not check whether
   862  // subexpressions within the rule should be kept.
   863  func (r *Rule) ShouldKeep() bool {
   864  	return ShouldKeep(r.expr)
   865  }
   866  
   867  // Kind returns the kind of rule this is (for example, "go_library").
   868  func (r *Rule) Kind() string {
   869  	return bzl.FormatString(r.kind)
   870  }
   871  
   872  // SetKind changes the kind of rule this is.
   873  func (r *Rule) SetKind(kind string) {
   874  	r.kind = &bzl.Ident{Name: kind}
   875  	r.updated = true
   876  }
   877  
   878  // Name returns the value of the rule's "name" attribute if it is a string
   879  // or "" if the attribute does not exist or is not a string.
   880  func (r *Rule) Name() string {
   881  	return r.AttrString("name")
   882  }
   883  
   884  // SetName sets the value of the rule's "name" attribute.
   885  func (r *Rule) SetName(name string) {
   886  	r.SetAttr("name", name)
   887  }
   888  
   889  // AttrKeys returns a sorted list of attribute keys used in this rule.
   890  func (r *Rule) AttrKeys() []string {
   891  	keys := make([]string, 0, len(r.attrs))
   892  	for k := range r.attrs {
   893  		keys = append(keys, k)
   894  	}
   895  	sort.SliceStable(keys, func(i, j int) bool {
   896  		if cmp := bt.NamePriority[keys[i]] - bt.NamePriority[keys[j]]; cmp != 0 {
   897  			return cmp < 0
   898  		}
   899  		return keys[i] < keys[j]
   900  	})
   901  	return keys
   902  }
   903  
   904  // Attr returns the value of the named attribute. nil is returned when the
   905  // attribute is not set.
   906  func (r *Rule) Attr(key string) bzl.Expr {
   907  	attr, ok := r.attrs[key]
   908  	if !ok {
   909  		return nil
   910  	}
   911  	return attr.expr.RHS
   912  }
   913  
   914  // AttrString returns the value of the named attribute if it is a scalar string.
   915  // "" is returned if the attribute is not set or is not a string.
   916  func (r *Rule) AttrString(key string) string {
   917  	attr, ok := r.attrs[key]
   918  	if !ok {
   919  		return ""
   920  	}
   921  	str, ok := attr.expr.RHS.(*bzl.StringExpr)
   922  	if !ok {
   923  		return ""
   924  	}
   925  	return str.Value
   926  }
   927  
   928  // AttrStrings returns the string values of an attribute if it is a list.
   929  // nil is returned if the attribute is not set or is not a list. Non-string
   930  // values within the list won't be returned.
   931  func (r *Rule) AttrStrings(key string) []string {
   932  	attr, ok := r.attrs[key]
   933  	if !ok {
   934  		return nil
   935  	}
   936  	list, ok := attr.expr.RHS.(*bzl.ListExpr)
   937  	if !ok {
   938  		return nil
   939  	}
   940  	strs := make([]string, 0, len(list.List))
   941  	for _, e := range list.List {
   942  		if str, ok := e.(*bzl.StringExpr); ok {
   943  			strs = append(strs, str.Value)
   944  		}
   945  	}
   946  	return strs
   947  }
   948  
   949  // DelAttr removes the named attribute from the rule.
   950  func (r *Rule) DelAttr(key string) {
   951  	delete(r.attrs, key)
   952  	r.updated = true
   953  }
   954  
   955  // SetAttr adds or replaces the named attribute with value. If the attribute is
   956  // mergeable, then the value must implement the Merger interface, or an error will
   957  // be returned.
   958  func (r *Rule) SetAttr(key string, value interface{}) {
   959  	rhs := ExprFromValue(value)
   960  	if attr, ok := r.attrs[key]; ok {
   961  		attr.expr.RHS = rhs
   962  		attr.val = value
   963  	} else {
   964  		r.attrs[key] = attrValue{
   965  			expr: &bzl.AssignExpr{
   966  				LHS: &bzl.Ident{Name: key},
   967  				RHS: rhs,
   968  				Op:  "=",
   969  			},
   970  			val: value,
   971  		}
   972  	}
   973  	r.updated = true
   974  }
   975  
   976  // AttrComments returns the comments for an attribute.
   977  // It can be used to attach comments like "do not sort".
   978  func (r *Rule) AttrComments(key string) *bzl.Comments {
   979  	attr, ok := r.attrs[key]
   980  	if !ok {
   981  		return nil
   982  	}
   983  	return attr.expr.Comment()
   984  }
   985  
   986  // PrivateAttrKeys returns a sorted list of private attribute names.
   987  func (r *Rule) PrivateAttrKeys() []string {
   988  	keys := make([]string, 0, len(r.private))
   989  	for k := range r.private {
   990  		keys = append(keys, k)
   991  	}
   992  	sort.Strings(keys)
   993  	return keys
   994  }
   995  
   996  // PrivateAttr return the private value associated with a key.
   997  func (r *Rule) PrivateAttr(key string) interface{} {
   998  	return r.private[key]
   999  }
  1000  
  1001  // SetPrivateAttr associates a value with a key. Unlike SetAttr, this value
  1002  // is not converted to a build syntax tree and will not be written to a build
  1003  // file.
  1004  func (r *Rule) SetPrivateAttr(key string, value interface{}) {
  1005  	r.private[key] = value
  1006  }
  1007  
  1008  // Args returns positional arguments passed to a rule.
  1009  func (r *Rule) Args() []bzl.Expr {
  1010  	return r.args
  1011  }
  1012  
  1013  // AddArg adds a positional argument to the rule.
  1014  func (r *Rule) AddArg(value bzl.Expr) {
  1015  	r.args = append(r.args, value)
  1016  	r.updated = true
  1017  }
  1018  
  1019  // UpdateArg replaces an existing arg with a new value.
  1020  func (r *Rule) UpdateArg(index int, value bzl.Expr) error {
  1021  	if len(r.args) < index {
  1022  		return fmt.Errorf("can't update argument at index %d, only have %d args", index, len(r.args))
  1023  	}
  1024  	r.args[index] = value
  1025  	r.updated = true
  1026  	return nil
  1027  }
  1028  
  1029  // SortedAttrs returns the keys of attributes whose values will be sorted
  1030  func (r *Rule) SortedAttrs() []string {
  1031  	return r.sortedAttrs
  1032  }
  1033  
  1034  // SetSortedAttrs sets the keys of attributes whose values will be sorted
  1035  func (r *Rule) SetSortedAttrs(keys []string) {
  1036  	r.sortedAttrs = keys
  1037  	r.updated = true
  1038  }
  1039  
  1040  // Insert marks this statement for insertion at the end of the file. Multiple
  1041  // statements will be inserted in the order Insert is called.
  1042  func (r *Rule) Insert(f *File) {
  1043  	var stmt []bzl.Expr
  1044  	if f.function == nil {
  1045  		stmt = f.File.Stmt
  1046  	} else {
  1047  		stmt = f.function.stmt.Body
  1048  	}
  1049  	r.InsertAt(f, len(stmt))
  1050  }
  1051  
  1052  // InsertAt marks this statement for insertion before the statement at index.
  1053  // Multiple rules inserted at the same index will be inserted in the order
  1054  // Insert is called. Loads inserted at the same index will be inserted first.
  1055  func (r *Rule) InsertAt(f *File, index int) {
  1056  	r.index = index
  1057  	r.inserted = true
  1058  	f.Rules = append(f.Rules, r)
  1059  }
  1060  
  1061  // IsEmpty returns true when the rule contains none of the attributes in attrs
  1062  // for its kind. attrs should contain attributes that make the rule buildable
  1063  // like srcs or deps and not descriptive attributes like name or visibility.
  1064  func (r *Rule) IsEmpty(info KindInfo) bool {
  1065  	if info.NonEmptyAttrs == nil {
  1066  		return false
  1067  	}
  1068  	for k := range info.NonEmptyAttrs {
  1069  		if _, ok := r.attrs[k]; ok {
  1070  			return false
  1071  		}
  1072  	}
  1073  	return true
  1074  }
  1075  
  1076  func (r *Rule) sync() {
  1077  	r.syncComments()
  1078  	if !r.updated {
  1079  		return
  1080  	}
  1081  	r.updated = false
  1082  
  1083  	for _, k := range r.sortedAttrs {
  1084  		attr, ok := r.attrs[k]
  1085  		_, isUnsorted := attr.val.(UnsortedStrings)
  1086  		if ok && !isUnsorted {
  1087  			bzl.Walk(attr.expr.RHS, sortExprLabels)
  1088  		}
  1089  	}
  1090  
  1091  	call := r.expr.(*bzl.CallExpr)
  1092  
  1093  	// update `call.X` (e.g.: "# gazelle:map_kind")
  1094  	call.X = createDotExpr(r.Kind())
  1095  
  1096  	if len(r.attrs) > 1 {
  1097  		call.ForceMultiLine = true
  1098  	}
  1099  
  1100  	list := make([]bzl.Expr, 0, len(r.args)+len(r.attrs))
  1101  	list = append(list, r.args...)
  1102  	for _, attr := range r.attrs {
  1103  		list = append(list, attr.expr)
  1104  	}
  1105  	sortedAttrs := list[len(r.args):]
  1106  	key := func(e bzl.Expr) string { return e.(*bzl.AssignExpr).LHS.(*bzl.Ident).Name }
  1107  	sort.SliceStable(sortedAttrs, func(i, j int) bool {
  1108  		ki := key(sortedAttrs[i])
  1109  		kj := key(sortedAttrs[j])
  1110  		if cmp := bt.NamePriority[ki] - bt.NamePriority[kj]; cmp != 0 {
  1111  			return cmp < 0
  1112  		}
  1113  		return ki < kj
  1114  	})
  1115  
  1116  	call.List = list
  1117  	r.updated = false
  1118  }
  1119  
  1120  // ShouldKeep returns whether e is marked with a "# keep" comment. Kept
  1121  // expressions should not be removed or modified.
  1122  func ShouldKeep(e bzl.Expr) bool {
  1123  	for _, c := range append(e.Comment().Before, e.Comment().Suffix...) {
  1124  		text := strings.TrimSpace(strings.TrimPrefix(c.Token, "#"))
  1125  		if text == "keep" || strings.HasPrefix(text, "keep: ") {
  1126  			return true
  1127  		}
  1128  	}
  1129  	return false
  1130  }
  1131  
  1132  // CheckInternalVisibility overrides the given visibility if the package is
  1133  // internal.
  1134  func CheckInternalVisibility(rel, visibility string) string {
  1135  	if strings.HasSuffix(rel, "/internal") {
  1136  		visibility = fmt.Sprintf("//%s:__subpackages__", rel[:len(rel)-len("/internal")])
  1137  	} else if i := strings.LastIndex(rel, "/internal/"); i >= 0 {
  1138  		visibility = fmt.Sprintf("//%s:__subpackages__", rel[:i])
  1139  	} else if strings.HasPrefix(rel, "internal/") || rel == "internal" {
  1140  		visibility = "//:__subpackages__"
  1141  	}
  1142  	return visibility
  1143  }
  1144  
  1145  type byAttrName []KeyValue
  1146  
  1147  var _ sort.Interface = byAttrName{}
  1148  
  1149  func (s byAttrName) Len() int {
  1150  	return len(s)
  1151  }
  1152  
  1153  func (s byAttrName) Less(i, j int) bool {
  1154  	if cmp := bt.NamePriority[s[i].Key] - bt.NamePriority[s[j].Key]; cmp != 0 {
  1155  		return cmp < 0
  1156  	}
  1157  	return s[i].Key < s[j].Key
  1158  }
  1159  
  1160  func (s byAttrName) Swap(i, j int) {
  1161  	s[i], s[j] = s[j], s[i]
  1162  }
  1163  
  1164  func checkFile(f *File) error {
  1165  	names := make(map[string]bool)
  1166  	for _, r := range f.Rules {
  1167  		name := r.Name()
  1168  		if name == "" {
  1169  			continue
  1170  		}
  1171  		if names[name] {
  1172  			return fmt.Errorf("%s: multiple rules have the name %q", f.Path, name)
  1173  		}
  1174  		names[name] = true
  1175  	}
  1176  	return nil
  1177  }