Skip to content

Commit c1806b6

Browse files
committed
Add a buildifier warning to enforce rule load location
1 parent a0444eb commit c1806b6

12 files changed

+132
-6
lines changed

WARNINGS.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Warning categories supported by buildifier's linter:
6464
* [`repository-name`](#repository-name)
6565
* [`return-value`](#return-value)
6666
* [`rule-impl-return`](#rule-impl-return)
67+
* [`rule-load-location`](#rule-load-location)
6768
* [`same-origin-load`](#same-origin-load)
6869
* [`skylark-comment`](#skylark-comment)
6970
* [`skylark-docstring`](#skylark-docstring)
@@ -1095,6 +1096,26 @@ consider using
10951096
[providers](https://docs.bazel.build/versions/main/skylark/rules.html#providers)
10961097
or lists of providers instead.
10971098

1099+
--------------------------------------------------------------------------------
1100+
1101+
## <a name="rule-load-location"></a>Rule must be loaded from a specific location
1102+
1103+
* Category name: `rule-load-location`
1104+
* Automatic fix: no
1105+
* [Suppress the warning](#suppress): `# buildifier: disable=rule-load-location`
1106+
1107+
Warns when a rule is loaded from a location other than the expected one.
1108+
Expected locations are specified in the tables file:
1109+
1110+
```json
1111+
{
1112+
"RuleLoadLocation": {
1113+
"genrule": "//:genrule.bzl"
1114+
}
1115+
}
1116+
```
1117+
1118+
10981119
--------------------------------------------------------------------------------
10991120

11001121
## <a name="same-origin-load"></a>Same label is used for multiple loads

buildifier/config/config_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func ExampleExample() {
102102
// "repository-name",
103103
// "return-value",
104104
// "rule-impl-return",
105+
// "rule-load-location",
105106
// "skylark-comment",
106107
// "skylark-docstring",
107108
// "string-iteration",
@@ -290,6 +291,7 @@ func TestValidate(t *testing.T) {
290291
"repository-name",
291292
"return-value",
292293
"rule-impl-return",
294+
"rule-load-location",
293295
"skylark-comment",
294296
"skylark-docstring",
295297
"string-iteration",
@@ -359,6 +361,7 @@ func TestValidate(t *testing.T) {
359361
"repository-name",
360362
"return-value",
361363
"rule-impl-return",
364+
"rule-load-location",
362365
"skylark-comment",
363366
"skylark-docstring",
364367
"string-iteration",
@@ -427,7 +430,7 @@ func TestValidate(t *testing.T) {
427430
"repository-name",
428431
"return-value",
429432
"rule-impl-return",
430-
433+
"rule-load-location",
431434
"skylark-comment",
432435
"skylark-docstring",
433436
"string-iteration",

buildifier/integration_test.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ cat > golden/.buildifier.example.json <<EOF
307307
"repository-name",
308308
"return-value",
309309
"rule-impl-return",
310+
"rule-load-location",
310311
"skylark-comment",
311312
"skylark-docstring",
312313
"string-iteration",

tables/jsonparser.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type Definitions struct {
3131
NamePriority map[string]int
3232
StripLabelLeadingSlashes bool
3333
ShortenAbsoluteLabelsToRelative bool
34+
RuleLoadLocation map[string]string
3435
}
3536

3637
// ParseJSONDefinitions reads and parses JSON table definitions from file.
@@ -55,9 +56,9 @@ func ParseAndUpdateJSONDefinitions(file string, merge bool) error {
5556
}
5657

5758
if merge {
58-
MergeTables(definitions.IsLabelArg, definitions.LabelDenylist, definitions.IsListArg, definitions.IsSortableListArg, definitions.SortableDenylist, definitions.SortableAllowlist, definitions.NamePriority, definitions.StripLabelLeadingSlashes, definitions.ShortenAbsoluteLabelsToRelative)
59+
MergeTables(definitions.IsLabelArg, definitions.LabelDenylist, definitions.IsListArg, definitions.IsSortableListArg, definitions.SortableDenylist, definitions.SortableAllowlist, definitions.NamePriority, definitions.StripLabelLeadingSlashes, definitions.ShortenAbsoluteLabelsToRelative, definitions.RuleLoadLocation)
5960
} else {
60-
OverrideTables(definitions.IsLabelArg, definitions.LabelDenylist, definitions.IsListArg, definitions.IsSortableListArg, definitions.SortableDenylist, definitions.SortableAllowlist, definitions.NamePriority, definitions.StripLabelLeadingSlashes, definitions.ShortenAbsoluteLabelsToRelative)
61+
OverrideTables(definitions.IsLabelArg, definitions.LabelDenylist, definitions.IsListArg, definitions.IsSortableListArg, definitions.SortableDenylist, definitions.SortableAllowlist, definitions.NamePriority, definitions.StripLabelLeadingSlashes, definitions.ShortenAbsoluteLabelsToRelative, definitions.RuleLoadLocation)
6162
}
6263
return nil
6364
}

tables/jsonparser_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func TestParseJSONDefinitions(t *testing.T) {
3737
SortableAllowlist: map[string]bool{},
3838
NamePriority: map[string]int{"name": -1},
3939
StripLabelLeadingSlashes: true,
40+
RuleLoadLocation: map[string]string{"genrule": "//:genrule.bzl"},
4041
}
4142
if !reflect.DeepEqual(expected, definitions) {
4243
t.Errorf("ParseJSONDefinitions(simple_tables.json) = %v; want %v", definitions, expected)

tables/tables.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,11 @@ var IsModuleOverride = map[string]bool{
283283
"single_version_override": true,
284284
}
285285

286+
// RuleLoadLocation contains custom locations for loading rules.
287+
var RuleLoadLocation = map[string]string{}
288+
286289
// OverrideTables allows a user of the build package to override the special-case rules. The user-provided tables replace the built-in tables.
287-
func OverrideTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sortAllowlist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool) {
290+
func OverrideTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sortAllowlist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool, ruleLoadLocation map[string]string) {
288291
IsLabelArg = labelArg
289292
LabelDenylist = denylist
290293
IsListArg = listArg
@@ -294,10 +297,11 @@ func OverrideTables(labelArg, denylist, listArg, sortableListArg, sortDenylist,
294297
NamePriority = namePriority
295298
StripLabelLeadingSlashes = stripLabelLeadingSlashes
296299
ShortenAbsoluteLabelsToRelative = shortenAbsoluteLabelsToRelative
300+
RuleLoadLocation = ruleLoadLocation
297301
}
298302

299303
// MergeTables allows a user of the build package to override the special-case rules. The user-provided tables are merged into the built-in tables.
300-
func MergeTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sortAllowlist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool) {
304+
func MergeTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sortAllowlist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool, ruleLoadLocation map[string]string) {
301305
for k, v := range labelArg {
302306
IsLabelArg[k] = v
303307
}
@@ -321,4 +325,7 @@ func MergeTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sor
321325
}
322326
StripLabelLeadingSlashes = stripLabelLeadingSlashes || StripLabelLeadingSlashes
323327
ShortenAbsoluteLabelsToRelative = shortenAbsoluteLabelsToRelative || ShortenAbsoluteLabelsToRelative
328+
for k, v := range ruleLoadLocation {
329+
RuleLoadLocation[k] = v
330+
}
324331
}

tables/testdata/simple_tables.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,8 @@
1919
"NamePriority": {
2020
"name": -1
2121
},
22-
"StripLabelLeadingSlashes": true
22+
"StripLabelLeadingSlashes": true,
23+
"RuleLoadLocation": {
24+
"genrule": "//:genrule.bzl"
25+
}
2326
}

warn/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ go_library(
1313
"warn_cosmetic.go",
1414
"warn_deprecated.go",
1515
"warn_docstring.go",
16+
"warn_load.go",
1617
"warn_macro.go",
1718
"warn_naming.go",
1819
"warn_operation.go",
@@ -41,6 +42,7 @@ go_test(
4142
"warn_cosmetic_test.go",
4243
"warn_deprecated_test.go",
4344
"warn_docstring_test.go",
45+
"warn_load_test.go",
4446
"warn_macro_test.go",
4547
"warn_naming_test.go",
4648
"warn_operation_test.go",

warn/docs/warnings.textproto

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,21 @@ warnings: {
811811
"or lists of providers instead."
812812
}
813813

814+
warnings: {
815+
name: "rule-load-location"
816+
header: "Rule must be loaded from a specific location"
817+
description:
818+
"Warns when a rule is loaded from a location other than the expected one.\n"
819+
"Expected locations are specified in the tables file:\n\n"
820+
"```json\n"
821+
"{\n"
822+
" \"RuleLoadLocation\": {\n"
823+
" \"genrule\": \"//:genrule.bzl\"\n"
824+
" }\n"
825+
"}\n"
826+
"```\n"
827+
}
828+
814829
warnings: {
815830
name: "same-origin-load"
816831
header: "Same label is used for multiple loads"

warn/warn.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ var FileWarningMap = map[string]func(f *build.File) []*LinterFinding{
174174
"redefined-variable": redefinedVariableWarning,
175175
"repository-name": repositoryNameWarning,
176176
"rule-impl-return": ruleImplReturnWarning,
177+
"rule-load-location": ruleLoadLocationWarning,
177178
"return-value": missingReturnValueWarning,
178179
"skylark-comment": skylarkCommentWarning,
179180
"skylark-docstring": skylarkDocstringWarning,

warn/warn_load.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package warn
2+
3+
import (
4+
"fmt"
5+
"github.com/bazelbuild/buildtools/build"
6+
"github.com/bazelbuild/buildtools/tables"
7+
)
8+
9+
func ruleLoadLocationWarning(f *build.File) []*LinterFinding {
10+
var findings []*LinterFinding
11+
12+
for stmtIndex := 0; stmtIndex < len(f.Stmt); stmtIndex++ {
13+
load, ok := f.Stmt[stmtIndex].(*build.LoadStmt)
14+
if !ok {
15+
continue
16+
}
17+
18+
for i := 0; i < len(load.To); i++ {
19+
from := load.From[i]
20+
21+
expectedLocation, ok := tables.RuleLoadLocation[from.Name]
22+
if !ok || expectedLocation == load.Module.Value {
23+
continue
24+
}
25+
26+
f := makeLinterFinding(load.From[i], fmt.Sprintf("Rule %q must be loaded from %v.", from.Name, expectedLocation))
27+
findings = append(findings, f)
28+
}
29+
30+
}
31+
return findings
32+
}

warn/warn_load_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
Copyright 2020 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package warn
18+
19+
import (
20+
"github.com/bazelbuild/buildtools/tables"
21+
"testing"
22+
)
23+
24+
func TestWarnLoadLocation(t *testing.T) {
25+
tables.RuleLoadLocation["s1"] = ":z.bzl"
26+
tables.RuleLoadLocation["s3"] = ":y.bzl"
27+
checkFindingsAndFix(t, "rule-load-location", `
28+
load(":f.bzl", "s1", "s2")
29+
load(":a.bzl", "s3")
30+
`, `
31+
load(":f.bzl", "s1", "s2")
32+
load(":a.bzl", "s3")
33+
`,
34+
[]string{
35+
":1: Rule \"s1\" must be loaded from :z.bzl.",
36+
":2: Rule \"s3\" must be loaded from :y.bzl.",
37+
},
38+
scopeEverywhere)
39+
}

0 commit comments

Comments
 (0)