Repository: bazelbuild/buildtools
Pull request #560 (2019-02-25 19:08:56)

Introduce separate Bzl and Default filetypes
Previously buildifier distinguished BUILD, WORKSPACE, and all other files (the latter were .bzl files). However buildifier can be applied to other types of Starlark files (e.g. copybara configuration files), and its bzl-specific lints shouldn't be applied to them.

This commit introduces TypeBzl reserved for Bazel's .bzl files, and fixes some checks (mostly the checks for Bazel build API usages) to not be applied to generic Starlark files.

vladmos: Updated a test, please re-approve.

Patch:
diff --git a/build/lex.go b/build/lex.go
index ce024a9..acbeaec 100644
--- a/build/lex.go
+++ b/build/lex.go
@@ -33,22 +33,26 @@ import (
 type FileType int
 
 const (
-	// TypeDefault represents .bzl or other Starlark files
+	// TypeDefault represents general Starlark files
 	TypeDefault FileType = 1 << iota
 	// TypeBuild represents BUILD files
 	TypeBuild
 	// TypeWorkspace represents WORKSPACE files
 	TypeWorkspace
+	// TypeBzl represents .bzl files
+	TypeBzl
 )
 
 func (t FileType) String() string {
 	switch t {
 	case TypeDefault:
-		return ".bzl"
+		return "default"
 	case TypeBuild:
 		return "BUILD"
 	case TypeWorkspace:
 		return "WORKSPACE"
+	case TypeBzl:
+		return ".bzl"
 	}
 	return "unknown"
 }
@@ -77,7 +81,19 @@ func ParseWorkspace(filename string, data []byte) (*File, error) {
 	return f, err
 }
 
-// ParseDefault parses a file, marks it as not a BUILD file (e.g. bzl file) and returns the corresponding parse tree.
+// ParseBzl parses a file, marks it as a .bzl file and returns the corresponding parse tree.
+//
+// The filename is used only for generating error messages.
+func ParseBzl(filename string, data []byte) (*File, error) {
+	in := newInput(filename, data)
+	f, err := in.parse()
+	if f != nil {
+		f.Type = TypeBzl
+	}
+	return f, err
+}
+
+// ParseDefault parses a file, marks it as a generic Starlark file and returns the corresponding parse tree.
 //
 // The filename is used only for generating error messages.
 func ParseDefault(filename string, data []byte) (*File, error) {
@@ -95,7 +111,10 @@ func getFileType(filename string) FileType {
 	}
 	basename := strings.ToLower(filepath.Base(filename))
 	ext := filepath.Ext(basename)
-	if ext == ".bzl" || ext == ".sky" {
+	switch ext {
+	case ".bzl":
+		return TypeBzl
+	case ".sky":
 		return TypeDefault
 	}
 	base := basename[:len(basename)-len(ext)]
@@ -118,6 +137,8 @@ func Parse(filename string, data []byte) (*File, error) {
 		return ParseBuild(filename, data)
 	case TypeWorkspace:
 		return ParseWorkspace(filename, data)
+	case TypeBzl:
+		return ParseBzl(filename, data)
 	}
 	return ParseDefault(filename, data)
 }
diff --git a/build/print.go b/build/print.go
index 0ee6ae5..7b93442 100644
--- a/build/print.go
+++ b/build/print.go
@@ -226,7 +226,7 @@ func (p *printer) compactStmt(s1, s2 Expr) bool {
 	} else if isCommentBlock(s1) || isCommentBlock(s2) {
 		// Standalone comment blocks shouldn't be attached to other statements
 		return false
-	} else if p.fileType != TypeDefault && p.level == 0 {
+	} else if (p.fileType == TypeBuild || p.fileType == TypeWorkspace) && p.level == 0 {
 		// Top-level statements in a BUILD or WORKSPACE file
 		return false
 	} else if isFunctionDefinition(s1) || isFunctionDefinition(s2) {
@@ -698,10 +698,10 @@ func (p *printer) useCompactMode(start *Position, list *[]Expr, end *End, mode s
 		return true
 	}
 
-	// In the Default printing mode try to keep the original printing style.
+	// In the Default and .bzl printing modes try to keep the original printing style.
 	// Non-top-level statements and lists of arguments of a function definition
 	// should also keep the original style regardless of the mode.
-	if (p.level != 0 || p.fileType == TypeDefault || mode == modeDef) && mode != modeLoad {
+	if (p.level != 0 || p.fileType == TypeDefault || p.fileType == TypeBzl || mode == modeDef) && mode != modeLoad {
 		// If every element (including the brackets) ends on the same line where the next element starts,
 		// use the compact mode, otherwise use multiline mode.
 		// If an node's line number is 0, it means it doesn't appear in the original file,
diff --git a/build/rewrite.go b/build/rewrite.go
index 9dafae6..5f372c7 100644
--- a/build/rewrite.go
+++ b/build/rewrite.go
@@ -118,7 +118,7 @@ func (info *RewriteInfo) String() string {
 // Each rewrite function can be either applied for BUILD files, other files (such as .bzl),
 // or all files.
 const (
-	scopeDefault = TypeDefault
+	scopeDefault = TypeDefault | TypeBzl // .bzl and generic Starlark files
 	scopeBuild   = TypeBuild | TypeWorkspace // BUILD and WORKSPACE files
 	scopeBoth    = scopeDefault | scopeBuild
 )
@@ -469,7 +469,7 @@ func sortStringLists(f *File, info *RewriteInfo) {
 					continue
 				}
 				context := rule + "." + key.Name
-				if !tables.IsSortableListArg[key.Name] || tables.SortableBlacklist[context] || f.Type == TypeDefault {
+				if !tables.IsSortableListArg[key.Name] || tables.SortableBlacklist[context] || f.Type == TypeDefault || f.Type == TypeBzl {
 					continue
 				}
 				if disabled("unsafesort") && !tables.SortableWhitelist[context] && !allowedSort(context) {
diff --git a/buildifier/buildifier.go b/buildifier/buildifier.go
index e26854e..91bebf7 100644
--- a/buildifier/buildifier.go
+++ b/buildifier/buildifier.go
@@ -54,7 +54,7 @@ var (
 	tablesPath    = flag.String("tables", "", "path to JSON file with custom table definitions which will replace the built-in tables")
 	addTablesPath = flag.String("add_tables", "", "path to JSON file with custom table definitions which will be merged with the built-in tables")
 	version       = flag.Bool("version", false, "Print the version of buildifier")
-	inputType     = flag.String("type", "auto", "Input file type: build (for BUILD files), bzl (for .bzl files), workspace (for WORKSPACE files), or auto (default, based on the filename)")
+	inputType     = flag.String("type", "auto", "Input file type: build (for BUILD files), bzl (for .bzl files), workspace (for WORKSPACE files), default (for generic Starlark files) or auto (default, based on the filename)")
 
 	// Debug flags passed through to rewrite.go
 	allowSort = stringList("allowsort", "additional sort contexts to treat as safe")
@@ -129,11 +129,11 @@ func main() {
 
 	// Check input type.
 	switch *inputType {
-	case "build", "bzl", "workspace", "auto":
+	case "build", "bzl", "workspace", "default", "auto":
 		// ok
 
 	default:
-		fmt.Fprintf(os.Stderr, "buildifier: unrecognized input type %s; valid types are build, bzl, workspace, auto\n", *inputType)
+		fmt.Fprintf(os.Stderr, "buildifier: unrecognized input type %s; valid types are build, bzl, workspace, default, auto\n", *inputType)
 		os.Exit(2)
 	}
 
@@ -482,6 +482,8 @@ func getParser(inputType string) func(filename string, data []byte) (*build.File
 	switch inputType {
 	case "build":
 		return build.ParseBuild
+	case "bzl":
+		return build.ParseBzl
 	case "auto":
 		return build.Parse
 	case "workspace":
diff --git a/warn/warn.go b/warn/warn.go
index 118e298..1d6709c 100644
--- a/warn/warn.go
+++ b/warn/warn.go
@@ -184,7 +184,7 @@ func FileWarnings(f *build.File, pkg string, enabledWarnings []string, fix bool)
 			if fn == nil {
 				log.Fatalf("unexpected warning %q", warn)
 			}
-			if f.Type == build.TypeDefault {
+			if f.Type != build.TypeBuild && f.Type != build.TypeWorkspace {
 				continue
 			}
 			for _, stmt := range f.Stmt {
diff --git a/warn/warn_bazel.go b/warn/warn_bazel.go
index 05eb1b4..c3786fa 100644
--- a/warn/warn_bazel.go
+++ b/warn/warn_bazel.go
@@ -12,6 +12,12 @@ import (
 
 func constantGlobWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
+
+	if f.Type == build.TypeDefault {
+		// Only applicable to Bazel files
+		return findings
+	}
+
 	edit.EditFunction(f, "glob", func(call *build.CallExpr, stk []build.Expr) build.Expr {
 		if len(call.List) == 0 {
 			return nil
@@ -76,7 +82,7 @@ func nativeInBuildFilesWarning(f *build.File, fix bool) []*Finding {
 func nativePackageWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
 
-	if f.Type != build.TypeDefault {
+	if f.Type != build.TypeBzl {
 		return findings
 	}
 
@@ -105,7 +111,7 @@ func nativePackageWarning(f *build.File, fix bool) []*Finding {
 
 func duplicatedNameWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
-	if f.Type == build.TypeDefault {
+	if f.Type == build.TypeBzl || f.Type == build.TypeDefault {
 		// Not applicable to .bzl files.
 		return findings
 	}
diff --git a/warn/warn_bazel_api.go b/warn/warn_bazel_api.go
index f81f9a0..69f795b 100644
--- a/warn/warn_bazel_api.go
+++ b/warn/warn_bazel_api.go
@@ -88,6 +88,10 @@ func isFunctionCall(expr build.Expr, name string) (*build.CallExpr, bool) {
 func globalVariableUsageCheck(f *build.File, category, global, alternative string, fix bool) []*Finding {
 	findings := []*Finding{}
 
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	var walk func(e *build.Expr, env *bzlenv.Environment)
 	walk = func(e *build.Expr, env *bzlenv.Environment) {
 		defer bzlenv.WalkOnceWithEnvironment(*e, env, walk)
@@ -123,6 +127,11 @@ func globalVariableUsageCheck(f *build.File, category, global, alternative strin
 // and adds a load statement if necessary.
 func notLoadedFunctionUsageCheck(f *build.File, category string, globals []string, loadFrom string, fix bool) []*Finding {
 	findings := []*Finding{}
+
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	toLoad := []string{}
 
 	var walk func(e *build.Expr, env *bzlenv.Environment)
@@ -194,6 +203,11 @@ func makeKeyword(argument build.Expr, name string) build.Expr {
 
 func attrConfigurationWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
+
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
 		// Find nodes that match the following pattern: attr.xxxx(..., cfg = "data", ...)
 		call, ok := expr.(*build.CallExpr)
@@ -230,6 +244,11 @@ func attrConfigurationWarning(f *build.File, fix bool) []*Finding {
 
 func attrNonEmptyWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
+
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
 		// Find nodes that match the following pattern: attr.xxxx(..., non_empty = ..., ...)
 		call, ok := expr.(*build.CallExpr)
@@ -263,6 +282,11 @@ func attrNonEmptyWarning(f *build.File, fix bool) []*Finding {
 
 func attrSingleFileWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
+
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
 		// Find nodes that match the following pattern: attr.xxxx(..., single_file = ..., ...)
 		call, ok := expr.(*build.CallExpr)
@@ -309,6 +333,10 @@ func attrSingleFileWarning(f *build.File, fix bool) []*Finding {
 func ctxActionsWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
 
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	addWarning := func(expr build.Expr, name string) {
 		start, end := expr.Span()
 		findings = append(findings,
@@ -380,6 +408,10 @@ func ctxActionsWarning(f *build.File, fix bool) []*Finding {
 func fileTypeWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
 
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	var walk func(e *build.Expr, env *bzlenv.Environment)
 	walk = func(e *build.Expr, env *bzlenv.Environment) {
 		defer bzlenv.WalkOnceWithEnvironment(*e, env, walk)
@@ -411,6 +443,11 @@ func repositoryNameWarning(f *build.File, fix bool) []*Finding {
 
 func outputGroupWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
+
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	build.Edit(f, func(expr build.Expr, stack []build.Expr) build.Expr {
 		// Find nodes that match the following pattern: ctx.attr.xxx.output_group
 		outputGroup, ok := (expr).(*build.DotExpr)
@@ -455,6 +492,11 @@ func nativeHTTPArchiveWarning(f *build.File, fix bool) []*Finding {
 
 func contextArgsAPIWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
+
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	types := detectTypes(f)
 
 	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
@@ -510,6 +552,11 @@ func contextArgsAPIWarning(f *build.File, fix bool) []*Finding {
 
 func attrOutputDefaultWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
+
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
 		// Find nodes that match the following pattern: attr.output(..., default = ...)
 		call, ok := expr.(*build.CallExpr)
@@ -538,6 +585,11 @@ func attrOutputDefaultWarning(f *build.File, fix bool) []*Finding {
 
 func attrLicenseWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
+
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
 		// Find nodes that match the following pattern: attr.license(...)
 		call, ok := expr.(*build.CallExpr)
@@ -564,6 +616,10 @@ func attrLicenseWarning(f *build.File, fix bool) []*Finding {
 func ruleImplReturnWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
 
+	if f.Type != build.TypeBzl {
+		return findings
+	}
+
 	// iterate over rules and collect rule implementation function names
 	implNames := make(map[string]bool)
 	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
diff --git a/warn/warn_control_flow.go b/warn/warn_control_flow.go
index d761ea2..ea35a55 100644
--- a/warn/warn_control_flow.go
+++ b/warn/warn_control_flow.go
@@ -203,7 +203,7 @@ func noEffectWarning(f *build.File, fix bool) []*Finding {
 // unusedVariableCheck checks for unused variables inside a given node `stmt` (either *build.File or
 // *build.DefStmt) and reports unused and already defined variables.
 func unusedVariableCheck(f *build.File, stmts []build.Expr, findings []*Finding) []*Finding {
-	if f.Type == build.TypeDefault {
+	if f.Type == build.TypeDefault || f.Type == build.TypeBzl {
 		// Not applicable to .bzl files, unused symbols may be loaded and used in other files.
 		return findings
 	}
diff --git a/warn/warn_cosmetic.go b/warn/warn_cosmetic.go
index fbd5d50..a12f14b 100644
--- a/warn/warn_cosmetic.go
+++ b/warn/warn_cosmetic.go
@@ -84,8 +84,8 @@ func packageOnTopWarning(f *build.File, fix bool) []*Finding {
 func loadOnTopWarning(f *build.File, fix bool) []*Finding {
 	findings := []*Finding{}
 
-	if f.Type != build.TypeDefault {
-		// Only applicable to .bzl files
+	if f.Type != build.TypeDefault && f.Type != build.TypeBzl {
+		// Only applicable to default and .bzl files
 		return findings
 	}
 
@@ -125,6 +125,13 @@ func loadOnTopWarning(f *build.File, fix bool) []*Finding {
 }
 
 func outOfOrderLoadWarning(f *build.File, fix bool) []*Finding {
+	findings := []*Finding{}
+
+	if f.Type == build.TypeWorkspace {
+		// Not applicable for WORKSPACE files
+		return findings
+	}
+
 	// compareLoadLabels compares two module names
 	compareLoadLabels := func(load1Label, load2Label string) bool {
 		// handle absolute labels with explicit repositories separately to
@@ -163,13 +170,6 @@ func outOfOrderLoadWarning(f *build.File, fix bool) []*Finding {
 		return isExplicitRepo1
 	}
 
-	findings := []*Finding{}
-
-	if f.Type == build.TypeWorkspace {
-		// Not applicable for WORKSPACE files
-		return findings
-	}
-
 	sortedLoads := []*build.LoadStmt{}
 	for i := 0; i < len(f.Stmt); i++ {
 		load, ok := f.Stmt[i].(*build.LoadStmt)
diff --git a/warn/warn_docstring.go b/warn/warn_docstring.go
index 57c44e9..14e924e 100644
--- a/warn/warn_docstring.go
+++ b/warn/warn_docstring.go
@@ -29,7 +29,7 @@ func getDocstring(stmts []build.Expr) (build.Expr, bool) {
 }
 
 func moduleDocstringWarning(f *build.File, fix bool) []*Finding {
-	if f.Type != build.TypeDefault {
+	if f.Type != build.TypeDefault && f.Type != build.TypeBzl {
 		return []*Finding{}
 	}
 	if stmt, ok := getDocstring(f.Stmt); stmt != nil && !ok {


Test patch:
diff --git a/build/lex_test.go b/build/lex_test.go
index ce35af3..c63f18f 100644
--- a/build/lex_test.go
+++ b/build/lex_test.go
@@ -26,7 +26,7 @@ func TestIsBuildFilename(t *testing.T) {
 		"build":           TypeBuild,
 		"bUIld":           TypeBuild,
 		"BUILD.bazel":     TypeBuild,
-		"build.bzl":       TypeDefault,
+		"build.bzl":       TypeBzl,
 		"build.sky":       TypeDefault,
 		"WORKSPACE":       TypeWorkspace,
 		"external.BUILD":  TypeBuild,
@@ -34,9 +34,9 @@ func TestIsBuildFilename(t *testing.T) {
 		"aBUILD":          TypeDefault,
 		"thing.sky":       TypeDefault,
 		"my.WORKSPACE":    TypeWorkspace,
-		"thing.bzl":       TypeDefault,
+		"thing.bzl":       TypeBzl,
 		"workspace.bazel": TypeWorkspace,
-		"workspace.bzl":   TypeDefault,
+		"workspace.bzl":   TypeBzl,
 		"foo.bar":         TypeDefault,
 	}
 	for name, fileType := range cases {
diff --git a/build/print_test.go b/build/print_test.go
index 178e40a..58987c1 100644
--- a/build/print_test.go
+++ b/build/print_test.go
@@ -165,25 +165,32 @@ func testPrint(t *testing.T, in, out string, isBuild bool) {
 	}
 
 	base := "testdata/" + filepath.Base(in)
-	parser := ParseDefault
+	parsers := map[string]func(string, []byte)(*File, error){
+		"bzl": ParseBzl,
+		"default": ParseDefault,
+	}
 	if isBuild {
-		parser = ParseBuild
+		parsers = map[string]func(string, []byte)(*File, error){
+			"build": ParseBuild,
+		}
 	}
 
-	file, err := parser(base, data)
-	if err != nil {
-		t.Error(err)
-		return
-	}
+	for name, parser := range parsers {
+		file, err := parser(base, data)
+		if err != nil {
+			t.Error(err)
+			return
+		}
 
-	Rewrite(file, nil)
+		Rewrite(file, nil)
 
-	ndata := Format(file)
+		ndata := Format(file)
 
-	if !bytes.Equal(ndata, golden) {
-		t.Errorf("formatted %s incorrectly: diff shows -%s, +ours", base, filepath.Base(out))
-		testutils.Tdiff(t, golden, ndata)
-		return
+		if !bytes.Equal(ndata, golden) {
+			t.Errorf("formatted %s with parser %s incorrectly: diff shows -%s, +ours", base, name, filepath.Base(out))
+			testutils.Tdiff(t, golden, ndata)
+			return
+		}
 	}
 }
 
diff --git a/warn/warn_bazel_api_test.go b/warn/warn_bazel_api_test.go
index 6297963..6d6f3f7 100644
--- a/warn/warn_bazel_api_test.go
+++ b/warn/warn_bazel_api_test.go
@@ -21,7 +21,7 @@ rule(
 
 attr.label_list(mandatory = True, cfg = "host")`,
 		[]string{`:3: cfg = "data" for attr definitions has no effect and should be removed.`},
-		scopeEverywhere)
+		scopeBzl)
 }
 
 func TestAttrNonEmptyWarning(t *testing.T) {
@@ -58,7 +58,7 @@ rule(
 			":7: non_empty attributes for attr definitions are deprecated in favor of allow_empty.",
 			":8: non_empty attributes for attr definitions are deprecated in favor of allow_empty.",
 		},
-		scopeEverywhere)
+		scopeBzl)
 }
 
 func TestAttrSingleFileWarning(t *testing.T) {
@@ -82,7 +82,7 @@ rule(
 			":4: single_file is deprecated in favor of allow_single_file.",
 			":5: single_file is deprecated in favor of allow_single_file.",
 		},
-		scopeEverywhere)
+		scopeBzl)
 }
 
 func TestCtxActionsWarning(t *testing.T) {
@@ -131,14 +131,14 @@ def impl(ctx):
 			`:12: "ctx.template_action" is deprecated.`,
 			`:13: "ctx.template_action" is deprecated.`,
 		},
-		scopeEverywhere)
+		scopeBzl)
 
 	checkFindings(t, "ctx-actions", `
 def impl(ctx):
   ctx.new_file(foo, bar, baz)
 `, []string{
 		`:2: "ctx.new_file" is deprecated.`,
-	}, scopeEverywhere)
+	}, scopeBzl)
 }
 
 func TestPackageNameWarning(t *testing.T) {
@@ -163,17 +163,17 @@ def g():
 			`:1: Global variable "PACKAGE_NAME" is deprecated in favor of "native.package_name()". Please rename it.`,
 			`:7: Global variable "PACKAGE_NAME" is deprecated in favor of "native.package_name()". Please rename it.`,
 		},
-		scopeEverywhere)
+		scopeBzl)
 
 	checkFindings(t, "package-name", `
 PACKAGE_NAME = "foo"
 foo(a = PACKAGE_NAME)
-`, []string{}, scopeEverywhere)
+`, []string{}, scopeBzl)
 
 	checkFindings(t, "package-name", `
 load(":foo.bzl", "PACKAGE_NAME")
 foo(a = PACKAGE_NAME)
-`, []string{}, scopeEverywhere)
+`, []string{}, scopeBzl)
 }
 
 func TestRepositoryNameWarning(t *testing.T) {
@@ -197,17 +197,17 @@ def g():
 		[]string{
 			`:1: Global variable "REPOSITORY_NAME" is deprecated in favor of "native.repository_name()". Please rename it.`,
 			`:7: Global variable "REPOSITORY_NAME" is deprecated in favor of "native.repository_name()". Please rename it.`,
-		}, scopeEverywhere)
+		}, scopeBzl)
 
 	checkFindings(t, "repository-name", `
 REPOSITORY_NAME = "foo"
 foo(a = REPOSITORY_NAME)
-`, []string{}, scopeEverywhere)
+`, []string{}, scopeBzl)
 
 	checkFindings(t, "repository-name", `
 load(":foo.bzl", "REPOSITORY_NAME")
 foo(a = REPOSITORY_NAME)
-`, []string{}, scopeEverywhere)
+`, []string{}, scopeBzl)
 }
 
 func TestFileTypeNameWarning(t *testing.T) {
@@ -228,14 +228,14 @@ def macro2():
 		":2: The FileType function is deprecated.",
 		":4: The FileType function is deprecated.",
 		":7: The FileType function is deprecated.",
-	}, scopeEverywhere)
+	}, scopeBzl)
 
 	checkFindings(t, "filetype", `
 FileType = foo
 
 rule1(types=FileType([".cc", ".h"]))
 rule2(types=FileType(types=[".cc", ".h"]))
-`, []string{}, scopeEverywhere)
+`, []string{}, scopeBzl)
 }
 
 func TestOutputGroupWarning(t *testing.T) {
@@ -249,7 +249,7 @@ def _impl(ctx):
 		[]string{
 			`:2: "ctx.attr.dep.output_group" is deprecated in favor of "ctx.attr.dep[OutputGroupInfo]".`,
 		},
-		scopeEverywhere)
+		scopeBzl)
 }
 
 func TestNativeGitRepositoryWarning(t *testing.T) {
@@ -269,7 +269,7 @@ def macro():
 		[]string{
 			`:4: Function "git_repository" is not global anymore and needs to be loaded from "@bazel_tools//tools/build_defs/repo:git.bzl".`,
 		},
-		scopeEverywhere)
+		scopeBzl)
 
 	checkFindingsAndFix(t, "git-repository", `
 """My file"""
@@ -290,7 +290,7 @@ def macro():
 			`:4: Function "git_repository" is not global anymore and needs to be loaded from "@bazel_tools//tools/build_defs/repo:git.bzl".`,
 			`:5: Function "new_git_repository" is not global anymore and needs to be loaded from "@bazel_tools//tools/build_defs/repo:git.bzl".`,
 		},
-		scopeEverywhere)
+		scopeBzl)
 
 	checkFindingsAndFix(t, "git-repository", `
 """My file"""
@@ -312,7 +312,7 @@ def macro():
 		[]string{
 			`:7: Function "new_git_repository" is not global anymore and needs to be loaded from "@bazel_tools//tools/build_defs/repo:git.bzl".`,
 		},
-		scopeEverywhere)
+		scopeBzl)
 }
 
 func TestNativeHttpArchiveWarning(t *testing.T) {
@@ -332,7 +332,7 @@ def macro():
 		[]string{
 			`:4: Function "http_archive" is not global anymore and needs to be loaded from "@bazel_tools//tools/build_defs/repo:http.bzl".`,
 		},
-		scopeEverywhere)
+		scopeBzl)
 }
 
 func TestContextArgsAPIWarning(t *testing.T) {
@@ -368,7 +368,7 @@ def impl(ctx):
 			`:9: "ctx.actions.args().add()" for multiple arguments is deprecated in favor of "add_all()" or "add_joined()".`,
 			`:10: "ctx.actions.args().add()" for multiple arguments is deprecated in favor of "add_all()" or "add_joined()".`,
 		},
-		scopeEverywhere)
+		scopeBzl)
 }
 
 func TestAttrOutputDefault(t *testing.T) {
@@ -384,7 +384,7 @@ rule(
 		[]string{
 			`:3: The "default" parameter for attr.output() is deprecated.`,
 		},
-		scopeEverywhere)
+		scopeBzl)
 }
 
 func TestAttrLicense(t *testing.T) {
@@ -399,7 +399,7 @@ rule(
 `, []string{
 		`:3: "attr.license()" is deprecated and shouldn't be used.`,
 		`:4: "attr.license()" is deprecated and shouldn't be used.`,
-	}, scopeEverywhere)
+	}, scopeBzl)
 }
 
 func TestRuleImplReturn(t *testing.T) {
@@ -410,7 +410,7 @@ def _impl(ctx):
 rule(implementation=_impl)
 `, []string{
 		`:2: Avoid using the legacy provider syntax.`,
-	}, scopeEverywhere)
+	}, scopeBzl)
 
 	checkFindings(t, "rule-impl-return", `
 def _impl(ctx):
@@ -421,14 +421,14 @@ def _impl(ctx):
 x = rule(_impl, attrs = {})
 `, []string{
 		`:3: Avoid using the legacy provider syntax.`,
-	}, scopeEverywhere)
+	}, scopeBzl)
 
 	checkFindings(t, "rule-impl-return", `
 def _impl(ctx):
   pass  # no return statements
 
 x = rule(_impl, attrs = {})
-`, []string{}, scopeEverywhere)
+`, []string{}, scopeBzl)
 
 	checkFindings(t, "rule-impl-return", `
 def _impl1():  # not used as a rule implementation function
@@ -455,5 +455,5 @@ rule(
 
 rule()  # no parameters
 rule(foo = bar)  # no matching parameters
-`, []string{}, scopeEverywhere)
+`, []string{}, scopeBzl)
 }
diff --git a/warn/warn_bazel_test.go b/warn/warn_bazel_test.go
index ed79950..8d9c49d 100644
--- a/warn/warn_bazel_test.go
+++ b/warn/warn_bazel_test.go
@@ -14,7 +14,7 @@ cc_library(srcs =
 )`,
 		[]string{":1: Glob pattern `foo.cc` has no wildcard",
 			":6: Glob pattern `test.cpp` has no wildcard"},
-		scopeEverywhere)
+		scopeBazel)
 }
 
 func TestNativeInBuildFiles(t *testing.T) {
diff --git a/warn/warn_cosmetic_test.go b/warn/warn_cosmetic_test.go
index 60d4495..0f4464f 100644
--- a/warn/warn_cosmetic_test.go
+++ b/warn/warn_cosmetic_test.go
@@ -113,7 +113,9 @@ load(":f.bzl", "x")
 foo()
 
 x()`,
-		[]string{":2: Load statements should be at the top of the file."}, scopeBzl)
+		[]string{
+			":2: Load statements should be at the top of the file.",
+		}, scopeDefault|scopeBzl)
 
 	checkFindingsAndFix(t, "load-on-top", `
 """Docstring"""
@@ -149,7 +151,7 @@ bar()`,
 		[]string{
 			":9: Load statements should be at the top of the file.",
 			":15: Load statements should be at the top of the file.",
-		}, scopeBzl)
+		}, scopeDefault|scopeBzl)
 }
 
 func TestOutOfOrderLoad(t *testing.T) {
@@ -167,7 +169,7 @@ b += 2
 load(":b.bzl", "b")
 a + b`,
 		[]string{":5: Load statement is out of its lexicographical order."},
-		scopeBuild|scopeBzl)
+		scopeBuild|scopeBzl|scopeDefault)
 
 	checkFindingsAndFix(t, "out-of-order-load", `
 # b comment
@@ -185,7 +187,7 @@ load(":b.bzl", "b")
 load(":c.bzl", "c")
 a + b + c`,
 		[]string{":6: Load statement is out of its lexicographical order."},
-		scopeBuild|scopeBzl)
+		scopeBuild|scopeBzl|scopeDefault)
 
 	checkFindingsAndFix(t, "out-of-order-load", `
 load(":a.bzl", "a")
@@ -205,7 +207,7 @@ load(":b.bzl", "b")
 			":2: Load statement is out of its lexicographical order.",
 			":3: Load statement is out of its lexicographical order.",
 			":6: Load statement is out of its lexicographical order.",
-		}, scopeBuild|scopeBzl)
+		}, scopeBuild|scopeBzl|scopeDefault)
 
 	checkFindingsAndFix(t, "out-of-order-load", `
 load(":a.bzl", "a")
@@ -213,7 +215,7 @@ load(":a.bzl", "a")
 `, `
 load(":a.bzl", "a")
 load(":a.bzl", "a")`,
-		[]string{}, scopeBuild|scopeBzl)
+		[]string{}, scopeBuild|scopeBzl|scopeDefault)
 
 	checkFindingsAndFix(t, "out-of-order-load", `
 load("//foo:xyz.bzl", "xyz")
@@ -221,7 +223,7 @@ load("//foo/bar:mno.bzl", "mno")
 `, `
 load("//foo:xyz.bzl", "xyz")
 load("//foo/bar:mno.bzl", "mno")`,
-		[]string{}, scopeBuild|scopeBzl)
+		[]string{}, scopeBuild|scopeBzl|scopeDefault)
 
 	checkFindingsAndFix(t, "out-of-order-load", `
 load("//foo:xyz.bzl", "xyz")
@@ -229,7 +231,7 @@ load("//foo2:mno.bzl", "mno")
 `, `
 load("//foo:xyz.bzl", "xyz")
 load("//foo2:mno.bzl", "mno")`,
-		[]string{}, scopeBuild|scopeBzl)
+		[]string{}, scopeBuild|scopeBzl|scopeDefault)
 
 	checkFindingsAndFix(t, "out-of-order-load", `
 load("//foo:b.bzl", "b")
@@ -237,16 +239,9 @@ load("//foo:a.bzl", "a")
 `, `
 load("//foo:a.bzl", "a")
 load("//foo:b.bzl", "b")`,
-		[]string{":2: Load statement is out of its lexicographical order."}, scopeBuild|scopeBzl)
-
-	checkFindingsAndFix(t, "out-of-order-load", `
-load("//foo.bzl", "foo")
-load("//bar.bzl", "bar")
-`, `
-load("//bar.bzl", "bar")
-load("//foo.bzl", "foo")
-`,
-		[]string{":2: Load statement is out of its lexicographical order."}, scopeBuild|scopeBzl)
+		[]string{
+			":2: Load statement is out of its lexicographical order.",
+		}, scopeBuild|scopeBzl|scopeDefault)
 }
 
 func TestUnsortedDictItems(t *testing.T) {
diff --git a/warn/warn_docstring_test.go b/warn/warn_docstring_test.go
index f40a7a3..1616356 100644
--- a/warn/warn_docstring_test.go
+++ b/warn/warn_docstring_test.go
@@ -5,12 +5,12 @@ import "testing"
 func TestModuleDocstring(t *testing.T) {
 	checkFindings(t, "module-docstring", ``,
 		[]string{},
-		scopeBzl)
+		scopeBzl|scopeDefault)
 
 	checkFindings(t, "module-docstring", `
 # empty file`,
 		[]string{},
-		scopeBzl)
+		scopeBzl|scopeDefault)
 
 	checkFindings(t, "module-docstring", `
 """This is the module"""
@@ -19,7 +19,7 @@ load("foo", "bar")
 
 bar()`,
 		[]string{},
-		scopeBzl)
+		scopeBzl|scopeDefault)
 
 	checkFindings(t, "module-docstring", `
 load("foo", "bar")
@@ -28,7 +28,7 @@ load("foo", "bar")
 
 bar()`,
 		[]string{":1: The file has no module docstring."},
-		scopeBzl)
+		scopeBzl|scopeDefault)
 
 	checkFindings(t, "module-docstring", `
 # comment
@@ -40,7 +40,7 @@ load("foo", "bar")
 
 bar()`,
 		[]string{},
-		scopeBzl)
+		scopeBzl|scopeDefault)
 
 	checkFindings(t, "module-docstring", `
 # comment
@@ -52,7 +52,7 @@ load("foo", "bar")
 
 bar()`,
 		[]string{":3: The file has no module docstring."},
-		scopeBzl)
+		scopeBzl|scopeDefault)
 }
 
 func TestFunctionDocstringExists(t *testing.T) {
diff --git a/warn/warn_test.go b/warn/warn_test.go
index 0c1424b..2989002 100644
--- a/warn/warn_test.go
+++ b/warn/warn_test.go
@@ -12,9 +12,11 @@ import (
 
 const (
 	scopeBuild      = build.TypeBuild
-	scopeBzl        = build.TypeDefault
+	scopeBzl        = build.TypeBzl
 	scopeWorkspace  = build.TypeWorkspace
-	scopeEverywhere = scopeBuild | scopeBzl | scopeWorkspace
+	scopeDefault    = build.TypeDefault
+	scopeEverywhere = scopeBuild | scopeBzl | scopeWorkspace | scopeDefault
+	scopeBazel      = scopeBuild | scopeBzl | scopeWorkspace
 )
 
 func getFilename(fileType build.FileType) string {
@@ -23,8 +25,10 @@ func getFilename(fileType build.FileType) string {
 		return "package/BUILD"
 	case build.TypeWorkspace:
 		return "package/WORKSPACE"
-	default:
+	case build.TypeBzl:
 		return "package/test_file.bzl"
+	default:
+		return "test_file.strlrk"
 	}
 }
 
@@ -101,6 +105,7 @@ func checkFindingsAndFix(t *testing.T, category, input, output string, expected
 		build.TypeDefault,
 		build.TypeBuild,
 		build.TypeWorkspace,
+		build.TypeBzl,
 	}
 
 	for _, fileType := range fileTypes {