Skip to content

Commit 7f228ee

Browse files
committed
Add a buildifier warning to enforce rule load location
1 parent a9c248f commit 7f228ee

12 files changed

+132
-6
lines changed

WARNINGS.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Warning categories supported by buildifier's linter:
7474
* [`repository-name`](#repository-name)
7575
* [`return-value`](#return-value)
7676
* [`rule-impl-return`](#rule-impl-return)
77+
* [`rule-load-location`](#rule-load-location)
7778
* [`same-origin-load`](#same-origin-load)
7879
* [`skylark-comment`](#skylark-comment)
7980
* [`skylark-docstring`](#skylark-docstring)
@@ -1210,6 +1211,26 @@ consider using
12101211
[providers](https://docs.bazel.build/versions/main/skylark/rules.html#providers)
12111212
or lists of providers instead.
12121213

1214+
--------------------------------------------------------------------------------
1215+
1216+
## <a name="rule-load-location"></a>Rule must be loaded from a specific location
1217+
1218+
* Category name: `rule-load-location`
1219+
* Automatic fix: no
1220+
* [Suppress the warning](#suppress): `# buildifier: disable=rule-load-location`
1221+
1222+
Warns when a rule is loaded from a location other than the expected one.
1223+
Expected locations are specified in the tables file:
1224+
1225+
```json
1226+
{
1227+
"RuleLoadLocation": {
1228+
"genrule": "//:genrule.bzl"
1229+
}
1230+
}
1231+
```
1232+
1233+
12131234
--------------------------------------------------------------------------------
12141235

12151236
## <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
@@ -112,6 +112,7 @@ func ExampleExample() {
112112
// "repository-name",
113113
// "return-value",
114114
// "rule-impl-return",
115+
// "rule-load-location",
115116
// "skylark-comment",
116117
// "skylark-docstring",
117118
// "string-iteration",
@@ -310,6 +311,7 @@ func TestValidate(t *testing.T) {
310311
"repository-name",
311312
"return-value",
312313
"rule-impl-return",
314+
"rule-load-location",
313315
"skylark-comment",
314316
"skylark-docstring",
315317
"string-iteration",
@@ -389,6 +391,7 @@ func TestValidate(t *testing.T) {
389391
"repository-name",
390392
"return-value",
391393
"rule-impl-return",
394+
"rule-load-location",
392395
"skylark-comment",
393396
"skylark-docstring",
394397
"string-iteration",
@@ -467,7 +470,7 @@ func TestValidate(t *testing.T) {
467470
"repository-name",
468471
"return-value",
469472
"rule-impl-return",
470-
473+
"rule-load-location",
471474
"skylark-comment",
472475
"skylark-docstring",
473476
"string-iteration",

buildifier/integration_test.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ cat > golden/.buildifier.example.json <<EOF
317317
"repository-name",
318318
"return-value",
319319
"rule-impl-return",
320+
"rule-load-location",
320321
"skylark-comment",
321322
"skylark-docstring",
322323
"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
@@ -271,8 +271,11 @@ var IsModuleOverride = map[string]bool{
271271
"single_version_override": true,
272272
}
273273

274+
// RuleLoadLocation contains custom locations for loading rules.
275+
var RuleLoadLocation = map[string]string{}
276+
274277
// OverrideTables allows a user of the build package to override the special-case rules. The user-provided tables replace the built-in tables.
275-
func OverrideTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sortAllowlist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool) {
278+
func OverrideTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sortAllowlist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool, ruleLoadLocation map[string]string) {
276279
IsLabelArg = labelArg
277280
LabelDenylist = denylist
278281
IsListArg = listArg
@@ -282,10 +285,11 @@ func OverrideTables(labelArg, denylist, listArg, sortableListArg, sortDenylist,
282285
NamePriority = namePriority
283286
StripLabelLeadingSlashes = stripLabelLeadingSlashes
284287
ShortenAbsoluteLabelsToRelative = shortenAbsoluteLabelsToRelative
288+
RuleLoadLocation = ruleLoadLocation
285289
}
286290

287291
// 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.
288-
func MergeTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sortAllowlist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool) {
292+
func MergeTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sortAllowlist map[string]bool, namePriority map[string]int, stripLabelLeadingSlashes, shortenAbsoluteLabelsToRelative bool, ruleLoadLocation map[string]string) {
289293
for k, v := range labelArg {
290294
IsLabelArg[k] = v
291295
}
@@ -309,4 +313,7 @@ func MergeTables(labelArg, denylist, listArg, sortableListArg, sortDenylist, sor
309313
}
310314
StripLabelLeadingSlashes = stripLabelLeadingSlashes || StripLabelLeadingSlashes
311315
ShortenAbsoluteLabelsToRelative = shortenAbsoluteLabelsToRelative || ShortenAbsoluteLabelsToRelative
316+
for k, v := range ruleLoadLocation {
317+
RuleLoadLocation[k] = v
318+
}
312319
}

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
@@ -897,6 +897,21 @@ warnings: {
897897
"or lists of providers instead."
898898
}
899899

900+
warnings: {
901+
name: "rule-load-location"
902+
header: "Rule must be loaded from a specific location"
903+
description:
904+
"Warns when a rule is loaded from a location other than the expected one.\n"
905+
"Expected locations are specified in the tables file:\n\n"
906+
"```json\n"
907+
"{\n"
908+
" \"RuleLoadLocation\": {\n"
909+
" \"genrule\": \"//:genrule.bzl\"\n"
910+
" }\n"
911+
"}\n"
912+
"```\n"
913+
}
914+
900915
warnings: {
901916
name: "same-origin-load"
902917
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
@@ -184,6 +184,7 @@ var FileWarningMap = map[string]func(f *build.File) []*LinterFinding{
184184
"redefined-variable": redefinedVariableWarning,
185185
"repository-name": repositoryNameWarning,
186186
"rule-impl-return": ruleImplReturnWarning,
187+
"rule-load-location": ruleLoadLocationWarning,
187188
"return-value": missingReturnValueWarning,
188189
"skylark-comment": skylarkCommentWarning,
189190
"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)