Skip to content

Commit

Permalink
Fix kinds and stmt (#1613)
Browse files Browse the repository at this point in the history
* fix: correct rule's kind & stmt

Created rule with `NewRule()` is different
to the rule created with `ruleFromExpr()`
(when parsing input data).

In the first case (`NewRule()`) kind is set to
`build.Ident{Name: string}` while it's parsed
expression in the second case (`ruleFromExpr()`).

For rules packed in a struct like
`selects.config_setting_group` there is no way
to create/generate the same rule as the one
parsed from the file as both will have
different kinds.

This change unifies kinds for two ways of
creating rules and correctly sets the identifier
while fixing loads (`FixLoads()`).

* chore: fix load tests

Change adds test to verify fixing load
statements while generating packed rules
in struct e.g. `selects.config_setting_group`.

* chore: update MODULE.bazel.lock
  • Loading branch information
birunts committed Sep 22, 2023
1 parent b2a81af commit 2090e57
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 4 deletions.
2 changes: 2 additions & 0 deletions internal/go_repository_tools_srcs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ GO_REPOSITORY_TOOLS_SRCS = [
Label("//internal/language:BUILD.bazel"),
Label("//internal/language/test_filegroup:BUILD.bazel"),
Label("//internal/language/test_filegroup:lang.go"),
Label("//internal/language/test_load_for_packed_rules:BUILD.bazel"),
Label("//internal/language/test_load_for_packed_rules:lang.go"),
Label("//internal/language/test_loads_from_flag:BUILD.bazel"),
Label("//internal/language/test_loads_from_flag:lang.go"),
Label("//internal:list_repository_tools_srcs.go"),
Expand Down
1 change: 1 addition & 0 deletions internal/language/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ filegroup(
srcs = [
"BUILD.bazel",
"//internal/language/test_filegroup:all_files",
"//internal/language/test_load_for_packed_rules:all_files",
"//internal/language/test_loads_from_flag:all_files",
],
visibility = ["//visibility:public"],
Expand Down
32 changes: 32 additions & 0 deletions internal/language/test_load_for_packed_rules/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "test_load_for_packed_rules",
srcs = ["lang.go"],
importpath = "github.com/bazelbuild/bazel-gazelle/internal/language/test_load_for_packed_rules",
visibility = ["//visibility:public"],
deps = [
"//config",
"//label",
"//language",
"//repo",
"//resolve",
"//rule",
],
)

filegroup(
name = "all_files",
testonly = True,
srcs = [
"BUILD.bazel",
"lang.go",
],
visibility = ["//visibility:public"],
)

alias(
name = "go_default_library",
actual = ":test_load_for_packed_rules",
visibility = ["//:__subpackages__"],
)
124 changes: 124 additions & 0 deletions internal/language/test_load_for_packed_rules/lang.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/* Copyright 2023 The Bazel Authors. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package `test_load_for_packed_rules` generates packed
// rule of `selects.config_setting_group`.
//
// This extension is experimental and subject to change. It is not included
// in the default Gazelle binary.
package test_load_for_packed_rules

import (
"context"

"github.com/bazelbuild/bazel-gazelle/config"
"github.com/bazelbuild/bazel-gazelle/label"
"github.com/bazelbuild/bazel-gazelle/language"
"github.com/bazelbuild/bazel-gazelle/repo"
"github.com/bazelbuild/bazel-gazelle/resolve"
"github.com/bazelbuild/bazel-gazelle/rule"
)

const testLoadForPackedRulesName = "test_load_for_packed_rules"

type testLoadForPackedRulesLang struct {
language.BaseLang

Initialized, RulesGenerated, DepsResolved bool
}

var (
_ language.Language = (*testLoadForPackedRulesLang)(nil)
_ language.LifecycleManager = (*testLoadForPackedRulesLang)(nil)
)

func NewLanguage() language.Language {
return &testLoadForPackedRulesLang{}
}

var kinds = map[string]rule.KindInfo{
"selects.config_setting_group": {
NonEmptyAttrs: map[string]bool{"name": true},
MergeableAttrs: map[string]bool{
"match_all": true,
"match_any": true,
},
},
}

var loads = []rule.LoadInfo{
{
Name: "@bazel_skylib//lib:selects.bzl",
Symbols: []string{
"selects",
},
},
}

func (*testLoadForPackedRulesLang) Name() string {
return testLoadForPackedRulesName
}

func (*testLoadForPackedRulesLang) Kinds() map[string]rule.KindInfo {
return kinds
}

func (*testLoadForPackedRulesLang) Loads() []rule.LoadInfo {
return loads
}

func (l *testLoadForPackedRulesLang) Before(ctx context.Context) {
l.Initialized = true
}

func (l *testLoadForPackedRulesLang) GenerateRules(args language.GenerateArgs) language.GenerateResult {
if !l.Initialized {
panic("GenerateRules must not be called before Before")
}
if l.RulesGenerated {
panic("GenerateRules must not be called after DoneGeneratingRules")
}

r := rule.NewRule("selects.config_setting_group", "all_configs_group")

match := []string{
"//:config_a",
"//:config_b",
}

r.SetAttr("match_all", match)

return language.GenerateResult{
Gen: []*rule.Rule{r},
Imports: []interface{}{nil},
}
}

func (l *testLoadForPackedRulesLang) DoneGeneratingRules() {
l.RulesGenerated = true
}

func (l *testLoadForPackedRulesLang) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.RemoteCache, r *rule.Rule, imports interface{}, from label.Label) {
if !l.RulesGenerated {
panic("Expected a call to DoneGeneratingRules before Resolve")
}
if l.DepsResolved {
panic("Resolve must be called before calling AfterResolvingDeps")
}
}

func (l *testLoadForPackedRulesLang) AfterResolvingDeps(ctx context.Context) {
l.DepsResolved = true
}
48 changes: 46 additions & 2 deletions rule/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -730,8 +730,9 @@ type attrValue struct {

// NewRule creates a new, empty rule with the given kind and name.
func NewRule(kind, name string) *Rule {
kindIdent := &bzl.Ident{Name: kind}
kindIdent := createDotExpr(kind)
call := &bzl.CallExpr{X: kindIdent}

r := &Rule{
stmt: stmt{expr: call},
kind: kindIdent,
Expand All @@ -752,6 +753,47 @@ func NewRule(kind, name string) *Rule {
return r
}

// Creates `bzl.Expr` for a kind which
// is either `*bzl.DotExpr` or `*bzl.Ident`.
//
// For `myKind` kind it returns:
// &bzl.Ident{
// Name: "myKind"
// }
//
// For `myKind.inner` kind it returns:
// &bzl.DotExpr{
// Name: "inner",
// X: &bzl.Ident {
// Name: "myKind"
// }
// }
func createDotExpr(kind string) bzl.Expr {
var expr bzl.Expr
parts := strings.Split(kind, ".")

if len(parts) > 1 {
// last `parts` element is the main bzl.DotExpr
var dotExpr *bzl.DotExpr = &bzl.DotExpr{Name: parts[len(parts)-1]}

_pDot := dotExpr

for idx := len(parts) - 2; idx > 0; idx-- {
d := &bzl.DotExpr{Name: parts[idx]}
_pDot.X = d
_pDot = d
}

// first `parts` element is the identifier
_pDot.X = &bzl.Ident{Name: parts[0]}
expr = dotExpr
} else {
expr = &bzl.Ident{Name: kind}
}

return expr
}

func isNestedDotOrIdent(expr bzl.Expr) bool {
if _, ok := expr.(*bzl.Ident); ok {
return true
Expand Down Expand Up @@ -999,7 +1041,9 @@ func (r *Rule) sync() {
}

call := r.expr.(*bzl.CallExpr)
call.X = r.kind

// update `call.X` (e.g.: "# gazelle:map_kind")
call.X = createDotExpr(r.Kind())

if len(r.attrs) > 1 {
call.ForceMultiLine = true
Expand Down
12 changes: 11 additions & 1 deletion tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ load(
"gazelle_generation_test",
)

load("//tests:tools.bzl", "get_binary")

# Exclude this entire directly from having anything gnerated by Gazelle. That
# way the test cases won't be fixed by `bazel run //:gazelle` when run in this
# repository.
Expand All @@ -26,10 +28,18 @@ gazelle_binary(
visibility = ["//visibility:private"],
)

gazelle_binary(
name = "gazelle_with_language_load_for_packed_rules",
languages = [
"//internal/language/test_load_for_packed_rules",
],
visibility = ["//visibility:private"],
)

[gazelle_generation_test(
# Name the test the path to the directory containing the WORKSPACE file.
name = file[0:-len("/WORKSPACE")],
gazelle_binary = ":gazelle" if "loads_from_flag" not in file else ":gazelle_with_language_loads_from_flag",
gazelle_binary = get_binary(file),
# This is a noop as the default is False. However, it does confirm that
# gazelle_generation_test accepts setting common test attributes.
local = False,
Expand Down
2 changes: 1 addition & 1 deletion tests/bcr/MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
9 changes: 9 additions & 0 deletions tests/fix_load_for_packed_rules/BUILD.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("@bazel_skylib//lib:selects.bzl", "selects")

selects.config_setting_group(
name = "all_configs_group",
match_all = [
"//:config_a",
"//:config_b",
],
)
3 changes: 3 additions & 0 deletions tests/fix_load_for_packed_rules/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Fix load statements for packed rules in struct

Fixes load statement for newly generated `select.config_setting_group` rule.
Empty file.
Empty file.
Empty file.
23 changes: 23 additions & 0 deletions tests/tools.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2023 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

def get_binary(target):
"""Get proper binary for test."""
if "fix_load_for_packed_rules" in target:
return ":gazelle_with_language_load_for_packed_rules"

if "loads_from_flag" in target:
return ":gazelle_with_language_loads_from_flag"

return ":gazelle"

0 comments on commit 2090e57

Please sign in to comment.