From ffaa2efdefa2fc67bf2199872ba946ba1514b5b7 Mon Sep 17 00:00:00 2001 From: Paul Johnston Date: Sun, 23 Jun 2024 20:46:49 -0600 Subject: [PATCH 1/5] Initial implementation of pkg/resolver/proto_package_wildcard_import_conflict_resolver.go --- README.md | 2 +- language/scala/scala_config.go | 4 +- language/scala/scala_rule.go | 28 ++--- pkg/parser/mocks/Parser.go | 2 +- pkg/resolver/BUILD.bazel | 2 + pkg/resolver/conflict_resolver.go | 7 +- pkg/resolver/import.go | 16 +++ pkg/resolver/import_map.go | 6 + pkg/resolver/mocks/ConflictResolver.go | 18 +-- pkg/resolver/mocks/Scope.go | 2 +- pkg/resolver/mocks/SymbolProvider.go | 2 +- pkg/resolver/mocks/SymbolResolver.go | 2 +- pkg/resolver/mocks/Universe.go | 2 +- .../predefined_label_conflict_resolver.go | 2 +- ...predefined_label_conflict_resolver_test.go | 3 +- ...ckage_wildcard_import_conflict_resolver.go | 100 ++++++++++++++++ ..._wildcard_import_conflict_resolver_test.go | 108 ++++++++++++++++++ .../scala_grpc_zio_conflict_resolver.go | 3 +- .../scala_grpc_zio_conflict_resolver_test.go | 3 +- .../scala_proto_package_conflict_resolver.go | 3 +- ...la_proto_package_conflict_resolver_test.go | 3 +- pkg/scalarule/mocks/ProviderRegistry.go | 2 +- 22 files changed, 275 insertions(+), 45 deletions(-) create mode 100644 pkg/resolver/proto_package_wildcard_import_conflict_resolver.go create mode 100644 pkg/resolver/proto_package_wildcard_import_conflict_resolver_test.go diff --git a/README.md b/README.md index b6b2b525..be3b79b0 100644 --- a/README.md +++ b/README.md @@ -588,7 +588,7 @@ Another way to resolve the conflict is to use a `resolver.ConflictResolver` impl type ConflictResolver interface { // ResolveConflict takes the context rule and imports, and the target symbol // with conflicts to resolve. - ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol) (*Symbol, bool) + ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol, from label.Label) (*Symbol, bool) } ``` diff --git a/language/scala/scala_config.go b/language/scala/scala_config.go index b3199b58..ca0855c5 100644 --- a/language/scala/scala_config.go +++ b/language/scala/scala_config.go @@ -121,9 +121,9 @@ func (c *scalaConfig) canProvide(from label.Label) bool { return false } -func (c *scalaConfig) resolveConflict(r *rule.Rule, imports resolver.ImportMap, imp *resolver.Import, symbol *resolver.Symbol) (*resolver.Symbol, bool) { +func (c *scalaConfig) resolveConflict(r *rule.Rule, imports resolver.ImportMap, imp *resolver.Import, symbol *resolver.Symbol, from label.Label) (*resolver.Symbol, bool) { for _, resolver := range c.conflictResolvers { - if resolved, ok := resolver.ResolveConflict(c.universe, r, imports, imp, symbol); ok { + if resolved, ok := resolver.ResolveConflict(c.universe, r, imports, imp, symbol, from); ok { return resolved, true } } diff --git a/language/scala/scala_rule.go b/language/scala/scala_rule.go index 793afbc5..40cb0243 100644 --- a/language/scala/scala_rule.go +++ b/language/scala/scala_rule.go @@ -131,8 +131,12 @@ func (r *scalaRule) ResolveImports(rctx *scalarule.ResolveContext) resolver.Impo item, _ := transitive.Pop() if len(item.sym.Conflicts) > 0 { - if resolved, ok := sc.resolveConflict(rctx.Rule, imports, item.imp, item.sym); ok { - item.imp.Symbol = resolved + if resolved, ok := sc.resolveConflict(rctx.Rule, imports, item.imp, item.sym, rctx.From); ok { + if resolved != nil { + item.imp.Symbol = resolved + } else { + continue // skip this item if conflict strategy says "ok" but returns nil (it's a wildcard import) + } } else { if r.ctx.scalaConfig.shouldAnnotateWildcardImports() && item.sym.Type == sppb.ImportType_PROTO_PACKAGE { if scope, ok := r.ctx.scope.GetScope(item.imp.Imp); ok { @@ -215,7 +219,7 @@ func (r *scalaRule) fileExports(file *sppb.File, exports resolver.ImportMap) { direct := resolver.NewTrieScope() putExport := func(imp *resolver.Import) { - if isSelfImport(imp, "", r.ctx.scalaConfig.rel, r.ctx.rule.Name()) { + if resolver.IsSelfImport(imp, "", r.ctx.scalaConfig.rel, r.ctx.rule.Name()) { if debugSelfImports { log.Println("skipping export from current", imp.Imp) } @@ -272,7 +276,7 @@ func (r *scalaRule) fileImports(imports resolver.ImportMap, file *sppb.File) { direct := resolver.NewTrieScope() putImport := func(imp *resolver.Import) { - if isSelfImport(imp, "", r.ctx.scalaConfig.rel, r.ctx.rule.Name()) { + if resolver.IsSelfImport(imp, "", r.ctx.scalaConfig.rel, r.ctx.rule.Name()) { if debugSelfImports { log.Println("skipping import from current", imp.Imp) } @@ -438,22 +442,6 @@ func isBinaryRule(kind string) bool { return strings.Contains(kind, "binary") || strings.Contains(kind, "test") } -func isSelfImport(imp *resolver.Import, repo, pkg, name string) bool { - if imp.Symbol == nil { - return false - } - if repo != "" { - return false - } - if pkg != imp.Symbol.Label.Pkg { - return false - } - if name != imp.Symbol.Label.Name { - return false - } - return true -} - func importBasename(imp string) string { index := strings.LastIndex(imp, ".") if index == -1 { diff --git a/pkg/parser/mocks/Parser.go b/pkg/parser/mocks/Parser.go index 15b4a0d4..e85eac36 100644 --- a/pkg/parser/mocks/Parser.go +++ b/pkg/parser/mocks/Parser.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.15.0. DO NOT EDIT. +// Code generated by mockery v2.16.0. DO NOT EDIT. package mocks diff --git a/pkg/resolver/BUILD.bazel b/pkg/resolver/BUILD.bazel index bffd4f64..c1b92ea6 100644 --- a/pkg/resolver/BUILD.bazel +++ b/pkg/resolver/BUILD.bazel @@ -17,6 +17,7 @@ go_library( "memo_symbol_resolver.go", "override_symbol_resolver.go", "predefined_label_conflict_resolver.go", + "proto_package_wildcard_import_conflict_resolver.go", "scala_grpc_zio_conflict_resolver.go", "scala_proto_package_conflict_resolver.go", "scala_scope.go", @@ -53,6 +54,7 @@ go_test( "chain_scope_test.go", "import_map_test.go", "predefined_label_conflict_resolver_test.go", + "proto_package_wildcard_import_conflict_resolver_test.go", "scala_grpc_zio_conflict_resolver_test.go", "scala_proto_package_conflict_resolver_test.go", "scala_scope_test.go", diff --git a/pkg/resolver/conflict_resolver.go b/pkg/resolver/conflict_resolver.go index 5a450855..896ef7bc 100644 --- a/pkg/resolver/conflict_resolver.go +++ b/pkg/resolver/conflict_resolver.go @@ -4,6 +4,7 @@ import ( "flag" "github.com/bazelbuild/bazel-gazelle/config" + "github.com/bazelbuild/bazel-gazelle/label" "github.com/bazelbuild/bazel-gazelle/rule" ) @@ -19,6 +20,8 @@ type ConflictResolver interface { // if the resolver is enabled. CheckFlags(fs *flag.FlagSet, c *config.Config) error // ResolveConflict takes the context rule and imports, and the target symbol - // with conflicts to resolve. - ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol) (*Symbol, bool) + // with conflicts to resolve. The ImportMap MAY be modified during the + // operation. The function MAY return (nil, true) in which case the symbol + // should be elided from further processing. + ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol, from label.Label) (*Symbol, bool) } diff --git a/pkg/resolver/import.go b/pkg/resolver/import.go index 92f6e2b9..b8329533 100644 --- a/pkg/resolver/import.go +++ b/pkg/resolver/import.go @@ -145,3 +145,19 @@ func (imp *Import) String() string { } return strings.Join(parts, " ") } + +func IsSelfImport(imp *Import, repo, pkg, name string) bool { + if imp.Symbol == nil { + return false + } + if repo != "" { + return false + } + if pkg != imp.Symbol.Label.Pkg { + return false + } + if name != imp.Symbol.Label.Name { + return false + } + return true +} diff --git a/pkg/resolver/import_map.go b/pkg/resolver/import_map.go index 6e3538e6..651276a6 100644 --- a/pkg/resolver/import_map.go +++ b/pkg/resolver/import_map.go @@ -70,6 +70,12 @@ func (imports ImportMap) Deps(from label.Label) (deps []label.Label) { return } +// Has checks if the given import key is already present in the map. +func (imports ImportMap) Has(imp string) bool { + _, ok := imports[imp] + return ok +} + // Put the given import in the map. func (imports ImportMap) Put(imp *Import) { // TODO: should we record *all* imports for a given key? Does priority matter? diff --git a/pkg/resolver/mocks/ConflictResolver.go b/pkg/resolver/mocks/ConflictResolver.go index 6a19e008..e3ceac1d 100644 --- a/pkg/resolver/mocks/ConflictResolver.go +++ b/pkg/resolver/mocks/ConflictResolver.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.15.0. DO NOT EDIT. +// Code generated by mockery v2.16.0. DO NOT EDIT. package mocks @@ -7,6 +7,8 @@ import ( config "github.com/bazelbuild/bazel-gazelle/config" + label "github.com/bazelbuild/bazel-gazelle/label" + mock "github.com/stretchr/testify/mock" resolver "github.com/stackb/scala-gazelle/pkg/resolver" @@ -52,13 +54,13 @@ func (_m *ConflictResolver) RegisterFlags(fs *flag.FlagSet, cmd string, c *confi _m.Called(fs, cmd, c) } -// ResolveConflict provides a mock function with given fields: universe, r, imports, imp, symbol -func (_m *ConflictResolver) ResolveConflict(universe resolver.Universe, r *rule.Rule, imports resolver.ImportMap, imp *resolver.Import, symbol *resolver.Symbol) (*resolver.Symbol, bool) { - ret := _m.Called(universe, r, imports, imp, symbol) +// ResolveConflict provides a mock function with given fields: universe, r, imports, imp, symbol, from +func (_m *ConflictResolver) ResolveConflict(universe resolver.Universe, r *rule.Rule, imports resolver.ImportMap, imp *resolver.Import, symbol *resolver.Symbol, from label.Label) (*resolver.Symbol, bool) { + ret := _m.Called(universe, r, imports, imp, symbol, from) var r0 *resolver.Symbol - if rf, ok := ret.Get(0).(func(resolver.Universe, *rule.Rule, resolver.ImportMap, *resolver.Import, *resolver.Symbol) *resolver.Symbol); ok { - r0 = rf(universe, r, imports, imp, symbol) + if rf, ok := ret.Get(0).(func(resolver.Universe, *rule.Rule, resolver.ImportMap, *resolver.Import, *resolver.Symbol, label.Label) *resolver.Symbol); ok { + r0 = rf(universe, r, imports, imp, symbol, from) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*resolver.Symbol) @@ -66,8 +68,8 @@ func (_m *ConflictResolver) ResolveConflict(universe resolver.Universe, r *rule. } var r1 bool - if rf, ok := ret.Get(1).(func(resolver.Universe, *rule.Rule, resolver.ImportMap, *resolver.Import, *resolver.Symbol) bool); ok { - r1 = rf(universe, r, imports, imp, symbol) + if rf, ok := ret.Get(1).(func(resolver.Universe, *rule.Rule, resolver.ImportMap, *resolver.Import, *resolver.Symbol, label.Label) bool); ok { + r1 = rf(universe, r, imports, imp, symbol, from) } else { r1 = ret.Get(1).(bool) } diff --git a/pkg/resolver/mocks/Scope.go b/pkg/resolver/mocks/Scope.go index 904037ba..31b0e8fe 100644 --- a/pkg/resolver/mocks/Scope.go +++ b/pkg/resolver/mocks/Scope.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.15.0. DO NOT EDIT. +// Code generated by mockery v2.16.0. DO NOT EDIT. package mocks diff --git a/pkg/resolver/mocks/SymbolProvider.go b/pkg/resolver/mocks/SymbolProvider.go index 09e5654b..acbcb9fa 100644 --- a/pkg/resolver/mocks/SymbolProvider.go +++ b/pkg/resolver/mocks/SymbolProvider.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.15.0. DO NOT EDIT. +// Code generated by mockery v2.16.0. DO NOT EDIT. package mocks diff --git a/pkg/resolver/mocks/SymbolResolver.go b/pkg/resolver/mocks/SymbolResolver.go index 9df76612..112daf98 100644 --- a/pkg/resolver/mocks/SymbolResolver.go +++ b/pkg/resolver/mocks/SymbolResolver.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.15.0. DO NOT EDIT. +// Code generated by mockery v2.16.0. DO NOT EDIT. package mocks diff --git a/pkg/resolver/mocks/Universe.go b/pkg/resolver/mocks/Universe.go index 18c28678..d37408c1 100644 --- a/pkg/resolver/mocks/Universe.go +++ b/pkg/resolver/mocks/Universe.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.15.0. DO NOT EDIT. +// Code generated by mockery v2.16.0. DO NOT EDIT. package mocks diff --git a/pkg/resolver/predefined_label_conflict_resolver.go b/pkg/resolver/predefined_label_conflict_resolver.go index 5977294a..6da08366 100644 --- a/pkg/resolver/predefined_label_conflict_resolver.go +++ b/pkg/resolver/predefined_label_conflict_resolver.go @@ -35,7 +35,7 @@ func (s *PredefinedLabelConflictResolver) CheckFlags(fs *flag.FlagSet, c *config // This implementation chooses symbols that have symbol.Label == label.NoLabel, // which is the scenario when a symbol is provided by the platform / compiler, // like "java.lang.String". -func (s *PredefinedLabelConflictResolver) ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol) (*Symbol, bool) { +func (s *PredefinedLabelConflictResolver) ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol, from label.Label) (*Symbol, bool) { if symbol.Label == label.NoLabel { return symbol, true } diff --git a/pkg/resolver/predefined_label_conflict_resolver_test.go b/pkg/resolver/predefined_label_conflict_resolver_test.go index 741ddc50..f9bc6c89 100644 --- a/pkg/resolver/predefined_label_conflict_resolver_test.go +++ b/pkg/resolver/predefined_label_conflict_resolver_test.go @@ -17,6 +17,7 @@ func TestPredefinedLabelConflictResolver(t *testing.T) { imports resolver.ImportMap imp resolver.Import name string + from label.Label want *resolver.Symbol wantOk bool }{ @@ -47,7 +48,7 @@ func TestPredefinedLabelConflictResolver(t *testing.T) { t.Run(name, func(t *testing.T) { universe := mocks.NewUniverse(t) resolver := resolver.PredefinedLabelConflictResolver{} - got, gotOk := resolver.ResolveConflict(universe, &tc.rule, tc.imports, &tc.imp, &tc.symbol) + got, gotOk := resolver.ResolveConflict(universe, &tc.rule, tc.imports, &tc.imp, &tc.symbol, tc.from) if diff := cmp.Diff(tc.wantOk, gotOk); diff != "" { t.Errorf("ok (-want +got):\n%s", diff) } diff --git a/pkg/resolver/proto_package_wildcard_import_conflict_resolver.go b/pkg/resolver/proto_package_wildcard_import_conflict_resolver.go new file mode 100644 index 00000000..c5723eff --- /dev/null +++ b/pkg/resolver/proto_package_wildcard_import_conflict_resolver.go @@ -0,0 +1,100 @@ +package resolver + +import ( + "flag" + "log" + "os" + "sort" + "strings" + + "github.com/bazelbuild/bazel-gazelle/config" + "github.com/bazelbuild/bazel-gazelle/label" + "github.com/bazelbuild/bazel-gazelle/rule" + + sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" +) + +func init() { + cr := &ProtoPackageWildcardImportConflictResolver{} + GlobalConflictResolverRegistry().PutConflictResolver(cr.Name(), cr) +} + +// ProtoPackageWildcardImportConflictResolver implements a strategy wherein a +// PROTO_PACKAGE symbol representing a wildcard import is "expanded" such that +// matching Names in the origin file are used to complete symbols in the given +// universe scope. +type ProtoPackageWildcardImportConflictResolver struct { +} + +// RegisterFlags implements part of the resolver.ConflictResolver interface. +func (s *ProtoPackageWildcardImportConflictResolver) Name() string { + return "proto_package_wildcard_import" +} + +// RegisterFlags implements part of the resolver.ConflictResolver interface. +func (s *ProtoPackageWildcardImportConflictResolver) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) { +} + +// CheckFlags implements part of the resolver.ConflictResolver interface. +func (s *ProtoPackageWildcardImportConflictResolver) CheckFlags(fs *flag.FlagSet, c *config.Config) error { + return nil +} + +// ResolveConflict implements part of the resolver.ConflictResolver interface. +// This implementation checks that the symbol is a proto package; if so, it uses +// the Names array of the source file to build a list of fully-qualified names +// that are found in the universe scope. Matching symbol are added to the +// import map. +func (s *ProtoPackageWildcardImportConflictResolver) ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol, from label.Label) (*Symbol, bool) { + if symbol.Type != sppb.ImportType_PROTO_PACKAGE { + return nil, false + } + if imp.Source == nil { + return nil, false + } + + file := imp.Source + prefix := strings.TrimSuffix(imp.Imp, "._") + + putImport := func(imp *Import) { + if IsSelfImport(imp, from.Repo, from.Pkg, r.Name()) { + return + } + if !imports.Has(imp.Imp) { + imports.Put(imp) + } + } + + names := make([]string, 0) + for _, name := range file.Names { + fqn := prefix + "." + name + if sym, ok := universe.GetSymbol(fqn); ok { + isProtoSymbol := sym.Type == sppb.ImportType_PROTO_ENUM || sym.Type == sppb.ImportType_PROTO_MESSAGE || sym.Type == sppb.ImportType_PROTO_SERVICE + if !isProtoSymbol { + continue + } + putImport(NewResolvedNameImport(fqn, file, name, sym)) + names = append(names, name) + } + } + + if len(names) > 0 && wantSuggestions() { + sort.Strings(names) + log.Printf( + "notice: in file %q the wildcard import %q should be replaced with:\nimport %s.{%s}", + file.Filename, + imp.Imp, + prefix, + strings.Join(names, ", "), + ) + } + + return nil, len(names) > 0 +} + +func wantSuggestions() bool { + if _, ok := os.LookupEnv("SCALA_GAZELLE_SUGGEST_PROTO_PACKAGE_WILDCARD_IMPORT_REPLACEMENTS"); ok { + return true + } + return false +} diff --git a/pkg/resolver/proto_package_wildcard_import_conflict_resolver_test.go b/pkg/resolver/proto_package_wildcard_import_conflict_resolver_test.go new file mode 100644 index 00000000..db00bc43 --- /dev/null +++ b/pkg/resolver/proto_package_wildcard_import_conflict_resolver_test.go @@ -0,0 +1,108 @@ +package resolver_test + +import ( + "testing" + + "github.com/bazelbuild/bazel-gazelle/label" + "github.com/bazelbuild/bazel-gazelle/rule" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stackb/scala-gazelle/pkg/resolver" + "github.com/stackb/scala-gazelle/pkg/resolver/mocks" + + sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" +) + +func TestProtoPackageWildcardImportConflictResolver(t *testing.T) { + for name, tc := range map[string]struct { + universe resolver.Universe + symbol resolver.Symbol + rule rule.Rule + imports resolver.ImportMap + imp resolver.Import + from label.Label + want *resolver.Symbol + wantImports resolver.ImportMap + wantOk bool + }{ + "degenerate": { + universe: mocks.NewUniverse(t), + symbol: resolver.Symbol{ + Name: "proto.api", + Type: sppb.ImportType_PROTO_PACKAGE, + Label: label.Label{Pkg: "proto/api", Name: "user_grpc_scala_library"}, + }, + }, + "typical": { + universe: func() resolver.Universe { + u := mocks.NewUniverse(t) + u.On("GetSymbol", "proto.api.User").Return( + &resolver.Symbol{ + Name: "proto.api.User", + }, + true, + ) + u.On("GetSymbol", "proto.api.Group").Return( + &resolver.Symbol{ + Name: "proto.api.Group", + }, + true, + ) + return u + }(), + from: label.New("", "api", "scala"), + symbol: resolver.Symbol{ + Name: "proto.api._", + Type: sppb.ImportType_PROTO_PACKAGE, + Label: label.Label{Pkg: "proto/api", Name: "user_grpc_scala_library"}, + }, + imp: resolver.Import{ + Imp: "proto.api._", + Source: &sppb.File{ + Names: []string{"User", "Group"}, + }, + }, + imports: resolver.NewImportMap(), + wantImports: resolver.NewImportMap( + &resolver.Import{ + Imp: "proto.api.User", + Kind: sppb.ImportKind_RESOLVED_NAME, + Source: &sppb.File{ + Names: []string{"User", "Group"}, + }, + Src: "User", + Symbol: &resolver.Symbol{ + Name: "proto.api.User", + }, + }, + &resolver.Import{ + Imp: "proto.api.Group", + Kind: sppb.ImportKind_RESOLVED_NAME, + Source: &sppb.File{ + Names: []string{"User", "Group"}, + }, + Src: "Group", + Symbol: &resolver.Symbol{ + Name: "proto.api.Group", + }, + }, + ), + wantOk: true, + want: nil, + }, + } { + t.Run(name, func(t *testing.T) { + rslv := resolver.ProtoPackageWildcardImportConflictResolver{} + got, gotOk := rslv.ResolveConflict(tc.universe, &tc.rule, tc.imports, &tc.imp, &tc.symbol, tc.from) + if diff := cmp.Diff(tc.wantOk, gotOk); diff != "" { + t.Errorf("got ok (-want +got):\n%s", diff) + } + if diff := cmp.Diff(tc.wantImports, tc.imports, cmpopts.IgnoreUnexported(sppb.File{}, resolver.Import{})); diff != "" { + t.Errorf("got importmap (-want +got):\n%s", diff) + } + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Errorf("got symbol (-want +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/resolver/scala_grpc_zio_conflict_resolver.go b/pkg/resolver/scala_grpc_zio_conflict_resolver.go index 58b56a81..0fae1805 100644 --- a/pkg/resolver/scala_grpc_zio_conflict_resolver.go +++ b/pkg/resolver/scala_grpc_zio_conflict_resolver.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/bazelbuild/bazel-gazelle/config" + "github.com/bazelbuild/bazel-gazelle/label" "github.com/bazelbuild/bazel-gazelle/rule" ) @@ -43,7 +44,7 @@ func (s *ScalaGrpcZioConflictResolver) CheckFlags(fs *flag.FlagSet, c *config.Co // using grpc, always resolve to conflict in favor of the grpc label, because // that rule will include the protos anyway. If they aren't using grpc, take // the proto rule so that the rule does not take on additional unnecessary deps. -func (s *ScalaGrpcZioConflictResolver) ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol) (*Symbol, bool) { +func (s *ScalaGrpcZioConflictResolver) ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol, from label.Label) (*Symbol, bool) { if len(symbol.Conflicts) != 1 { return nil, false } diff --git a/pkg/resolver/scala_grpc_zio_conflict_resolver_test.go b/pkg/resolver/scala_grpc_zio_conflict_resolver_test.go index e88b1694..998e3ef7 100644 --- a/pkg/resolver/scala_grpc_zio_conflict_resolver_test.go +++ b/pkg/resolver/scala_grpc_zio_conflict_resolver_test.go @@ -18,6 +18,7 @@ func TestScalaGrpcZioConflictResolver(t *testing.T) { rule rule.Rule imports resolver.ImportMap imp resolver.Import + from label.Label want *resolver.Symbol wantOk bool }{ @@ -104,7 +105,7 @@ func TestScalaGrpcZioConflictResolver(t *testing.T) { t.Run(name, func(t *testing.T) { universe := mocks.NewUniverse(t) resolver := resolver.ScalaGrpcZioConflictResolver{} - got, gotOk := resolver.ResolveConflict(universe, &tc.rule, tc.imports, &tc.imp, &tc.symbol) + got, gotOk := resolver.ResolveConflict(universe, &tc.rule, tc.imports, &tc.imp, &tc.symbol, tc.from) if diff := cmp.Diff(tc.wantOk, gotOk); diff != "" { t.Errorf("ok (-want +got):\n%s", diff) } diff --git a/pkg/resolver/scala_proto_package_conflict_resolver.go b/pkg/resolver/scala_proto_package_conflict_resolver.go index 6f5bdb9c..ab80d8b2 100644 --- a/pkg/resolver/scala_proto_package_conflict_resolver.go +++ b/pkg/resolver/scala_proto_package_conflict_resolver.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/bazelbuild/bazel-gazelle/config" + "github.com/bazelbuild/bazel-gazelle/label" "github.com/bazelbuild/bazel-gazelle/rule" sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" @@ -57,7 +58,7 @@ func (s *ScalaProtoPackageConflictResolver) CheckFlags(fs *flag.FlagSet, c *conf // using grpc, always resolve to conflict in favor of the grpc label, because // that rule will include the protos anyway. If they aren't using grpc, take // the proto rule so that the rule does not take on additional unnecessary deps. -func (s *ScalaProtoPackageConflictResolver) ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol) (*Symbol, bool) { +func (s *ScalaProtoPackageConflictResolver) ResolveConflict(universe Universe, r *rule.Rule, imports ImportMap, imp *Import, symbol *Symbol, from label.Label) (*Symbol, bool) { if len(symbol.Conflicts) != 1 { return nil, false } diff --git a/pkg/resolver/scala_proto_package_conflict_resolver_test.go b/pkg/resolver/scala_proto_package_conflict_resolver_test.go index 59ce7d49..229f7b13 100644 --- a/pkg/resolver/scala_proto_package_conflict_resolver_test.go +++ b/pkg/resolver/scala_proto_package_conflict_resolver_test.go @@ -18,6 +18,7 @@ func TestScalaProtoPackageConflictResolver(t *testing.T) { rule rule.Rule imports resolver.ImportMap imp resolver.Import + from label.Label want *resolver.Symbol wantOk bool }{ @@ -104,7 +105,7 @@ func TestScalaProtoPackageConflictResolver(t *testing.T) { t.Run(name, func(t *testing.T) { universe := mocks.NewUniverse(t) resolver := resolver.ScalaProtoPackageConflictResolver{} - got, gotOk := resolver.ResolveConflict(universe, &tc.rule, tc.imports, &tc.imp, &tc.symbol) + got, gotOk := resolver.ResolveConflict(universe, &tc.rule, tc.imports, &tc.imp, &tc.symbol, tc.from) if diff := cmp.Diff(tc.wantOk, gotOk); diff != "" { t.Errorf("ok (-want +got):\n%s", diff) } diff --git a/pkg/scalarule/mocks/ProviderRegistry.go b/pkg/scalarule/mocks/ProviderRegistry.go index 91c44c2c..93ff78fa 100644 --- a/pkg/scalarule/mocks/ProviderRegistry.go +++ b/pkg/scalarule/mocks/ProviderRegistry.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.15.0. DO NOT EDIT. +// Code generated by mockery v2.16.0. DO NOT EDIT. package mocks From 73c423c53e18760ec4d98c0376612221e2aac51c Mon Sep 17 00:00:00 2001 From: Paul Johnston Date: Mon, 24 Jun 2024 12:48:45 -0600 Subject: [PATCH 2/5] checkpoint remove package symbols --- Makefile | 8 +- build/stack/gazelle/scala/parse/file.pb.go | 218 ++++++++++++++++----- build/stack/gazelle/scala/parse/file.proto | 17 ++ build/stack/gazelle/scala/parse/rule.pb.go | 73 ++++--- build/stack/gazelle/scala/parse/rule.proto | 4 + language/scala/BUILD.bazel | 1 + language/scala/generate.go | 1 + language/scala/known_file_registry.go | 42 ++++ language/scala/language.go | 3 + language/scala/lifecycle.go | 18 +- language/scala/scala_rule.go | 6 + pkg/resolver/BUILD.bazel | 2 + pkg/resolver/import.go | 16 ++ pkg/resolver/import_map.go | 9 + pkg/resolver/known_file_registry.go | 16 ++ pkg/resolver/symbol.go | 6 + 16 files changed, 363 insertions(+), 77 deletions(-) create mode 100644 language/scala/known_file_registry.go create mode 100644 pkg/resolver/known_file_registry.go diff --git a/Makefile b/Makefile index 768959ac..6b88c93d 100644 --- a/Makefile +++ b/Makefile @@ -26,8 +26,14 @@ scalacache_protos: mv build/stack/gazelle/scala/cache/build/stack/gazelle/scala/cache/*.go build/stack/gazelle/scala/cache/ rm -rf build/stack/gazelle/scala/cache/build +.PHONY: autokeep_protos +autokeep_protos: + bazel run //build/stack/gazelle/scala/autokeep:autokeep_go_compiled_sources.update + mv build/stack/gazelle/scala/autokeep/build/stack/gazelle/scala/autokeep/*.go build/stack/gazelle/scala/autokeep/ + rm -rf build/stack/gazelle/scala/autokeep/build + .PHONY: protos -protos: jarindex_protos parser_protos scalacache_protos +protos: jarindex_protos parser_protos scalacache_protos autokeep_protos echo "Done." .PHONY: tidy diff --git a/build/stack/gazelle/scala/parse/file.pb.go b/build/stack/gazelle/scala/parse/file.pb.go index 77ee7ad6..c9207fb1 100644 --- a/build/stack/gazelle/scala/parse/file.pb.go +++ b/build/stack/gazelle/scala/parse/file.pb.go @@ -194,6 +194,93 @@ func (x *ClassList) GetClasses() []string { return nil } +type Import struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Kind ImportKind `protobuf:"varint,1,opt,name=kind,proto3,enum=build.stack.gazelle.scala.parse.ImportKind" json:"kind,omitempty"` + Imp string `protobuf:"bytes,2,opt,name=imp,proto3" json:"imp,omitempty"` + SourceFile string `protobuf:"bytes,3,opt,name=source_file,json=sourceFile,proto3" json:"source_file,omitempty"` + Src string `protobuf:"bytes,4,opt,name=src,proto3" json:"src,omitempty"` + Symbol *Symbol `protobuf:"bytes,5,opt,name=symbol,proto3" json:"symbol,omitempty"` + Error string `protobuf:"bytes,6,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *Import) Reset() { + *x = Import{} + if protoimpl.UnsafeEnabled { + mi := &file_build_stack_gazelle_scala_parse_file_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Import) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Import) ProtoMessage() {} + +func (x *Import) ProtoReflect() protoreflect.Message { + mi := &file_build_stack_gazelle_scala_parse_file_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Import.ProtoReflect.Descriptor instead. +func (*Import) Descriptor() ([]byte, []int) { + return file_build_stack_gazelle_scala_parse_file_proto_rawDescGZIP(), []int{2} +} + +func (x *Import) GetKind() ImportKind { + if x != nil { + return x.Kind + } + return ImportKind_IMPORT_KIND_UNKNOWN +} + +func (x *Import) GetImp() string { + if x != nil { + return x.Imp + } + return "" +} + +func (x *Import) GetSourceFile() string { + if x != nil { + return x.SourceFile + } + return "" +} + +func (x *Import) GetSrc() string { + if x != nil { + return x.Src + } + return "" +} + +func (x *Import) GetSymbol() *Symbol { + if x != nil { + return x.Symbol + } + return nil +} + +func (x *Import) GetError() string { + if x != nil { + return x.Error + } + return "" +} + var File_build_stack_gazelle_scala_parse_file_proto protoreflect.FileDescriptor var file_build_stack_gazelle_scala_parse_file_proto_rawDesc = []byte{ @@ -204,44 +291,61 @@ var file_build_stack_gazelle_scala_parse_file_proto_rawDesc = []byte{ 0x65, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x1a, 0x2c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x2f, 0x73, - 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb0, 0x03, 0x0a, 0x04, - 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x07, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, - 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, - 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, - 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x72, - 0x61, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x61, 0x69, - 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x76, 0x61, 0x6c, 0x73, - 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x14, 0x0a, 0x05, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x73, 0x74, 0x61, 0x63, - 0x6b, 0x2e, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2e, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x73, - 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x66, 0x0a, 0x0c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x40, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, - 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2e, 0x73, 0x63, - 0x61, 0x6c, 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x25, - 0x0a, 0x09, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, - 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, - 0x61, 0x73, 0x73, 0x65, 0x73, 0x42, 0x6a, 0x0a, 0x1f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x73, - 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2e, 0x73, 0x63, 0x61, - 0x6c, 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x50, 0x01, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x62, 0x2f, 0x73, 0x63, - 0x61, 0x6c, 0x61, 0x2d, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2f, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2f, - 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x3b, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2c, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, + 0x2f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x2f, 0x69, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb0, 0x03, 0x0a, 0x04, 0x46, 0x69, + 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x07, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x07, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x69, + 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x76, 0x61, 0x6c, 0x73, 0x18, 0x09, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x12, 0x4c, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x32, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, + 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2e, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x66, 0x0a, 0x0c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x40, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x73, 0x74, + 0x61, 0x63, 0x6b, 0x2e, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2e, 0x73, 0x63, 0x61, 0x6c, + 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x25, 0x0a, 0x09, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x06, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3f, + 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x67, 0x61, 0x7a, 0x65, 0x6c, + 0x6c, 0x65, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x49, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, + 0x10, 0x0a, 0x03, 0x69, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x69, 0x6d, + 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, + 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x73, 0x72, 0x63, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x73, 0x74, 0x61, + 0x63, 0x6b, 0x2e, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, + 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x52, 0x06, 0x73, + 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x6a, 0x0a, 0x1f, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x67, 0x61, 0x7a, 0x65, 0x6c, + 0x6c, 0x65, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x50, 0x01, + 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x61, + 0x63, 0x6b, 0x62, 0x2f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2d, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, + 0x65, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x67, 0x61, + 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2f, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x3b, 0x70, 0x61, 0x72, 0x73, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -256,20 +360,25 @@ func file_build_stack_gazelle_scala_parse_file_proto_rawDescGZIP() []byte { return file_build_stack_gazelle_scala_parse_file_proto_rawDescData } -var file_build_stack_gazelle_scala_parse_file_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_build_stack_gazelle_scala_parse_file_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_build_stack_gazelle_scala_parse_file_proto_goTypes = []interface{}{ (*File)(nil), // 0: build.stack.gazelle.scala.parse.File (*ClassList)(nil), // 1: build.stack.gazelle.scala.parse.ClassList - nil, // 2: build.stack.gazelle.scala.parse.File.ExtendsEntry + (*Import)(nil), // 2: build.stack.gazelle.scala.parse.Import + nil, // 3: build.stack.gazelle.scala.parse.File.ExtendsEntry + (ImportKind)(0), // 4: build.stack.gazelle.scala.parse.ImportKind + (*Symbol)(nil), // 5: build.stack.gazelle.scala.parse.Symbol } var file_build_stack_gazelle_scala_parse_file_proto_depIdxs = []int32{ - 2, // 0: build.stack.gazelle.scala.parse.File.extends:type_name -> build.stack.gazelle.scala.parse.File.ExtendsEntry - 1, // 1: build.stack.gazelle.scala.parse.File.ExtendsEntry.value:type_name -> build.stack.gazelle.scala.parse.ClassList - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 3, // 0: build.stack.gazelle.scala.parse.File.extends:type_name -> build.stack.gazelle.scala.parse.File.ExtendsEntry + 4, // 1: build.stack.gazelle.scala.parse.Import.kind:type_name -> build.stack.gazelle.scala.parse.ImportKind + 5, // 2: build.stack.gazelle.scala.parse.Import.symbol:type_name -> build.stack.gazelle.scala.parse.Symbol + 1, // 3: build.stack.gazelle.scala.parse.File.ExtendsEntry.value:type_name -> build.stack.gazelle.scala.parse.ClassList + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_build_stack_gazelle_scala_parse_file_proto_init() } @@ -278,6 +387,7 @@ func file_build_stack_gazelle_scala_parse_file_proto_init() { return } file_build_stack_gazelle_scala_parse_symbol_proto_init() + file_build_stack_gazelle_scala_parse_import_proto_init() if !protoimpl.UnsafeEnabled { file_build_stack_gazelle_scala_parse_file_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*File); i { @@ -303,6 +413,18 @@ func file_build_stack_gazelle_scala_parse_file_proto_init() { return nil } } + file_build_stack_gazelle_scala_parse_file_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Import); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -310,7 +432,7 @@ func file_build_stack_gazelle_scala_parse_file_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_build_stack_gazelle_scala_parse_file_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/build/stack/gazelle/scala/parse/file.proto b/build/stack/gazelle/scala/parse/file.proto index c646764a..31fd95eb 100644 --- a/build/stack/gazelle/scala/parse/file.proto +++ b/build/stack/gazelle/scala/parse/file.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package build.stack.gazelle.scala.parse; import "build/stack/gazelle/scala/parse/symbol.proto"; +import "build/stack/gazelle/scala/parse/import.proto"; option go_package = "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse;parse"; option java_package = "build.stack.gazelle.scala.parse"; @@ -40,3 +41,19 @@ message File { message ClassList { repeated string classes = 1; } + +// Import represents a resolved import/export. +message Import { + // Kind is the import type + ImportKind kind = 1; + // Imp is the name of the import + string imp = 2; + // File is the source file (when this is a direct import). + string source_file = 3; + // Src is the name of the parent import (when this is an implicit import) + string src = 4; + // Symbol is the resolved symbol of the import, or nil if not resolved. + Symbol symbol = 5; + // Error is assiged if there is a resolution error. + string error = 6; +} diff --git a/build/stack/gazelle/scala/parse/rule.pb.go b/build/stack/gazelle/scala/parse/rule.pb.go index a36df1cc..e7539870 100644 --- a/build/stack/gazelle/scala/parse/rule.pb.go +++ b/build/stack/gazelle/scala/parse/rule.pb.go @@ -25,11 +25,13 @@ type Rule struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Label string `protobuf:"bytes,1,opt,name=label,proto3" json:"label,omitempty"` - Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` - Files []*File `protobuf:"bytes,3,rep,name=files,proto3" json:"files,omitempty"` - Sha256 string `protobuf:"bytes,4,opt,name=sha256,proto3" json:"sha256,omitempty"` - ParseTimeMillis int64 `protobuf:"varint,5,opt,name=parse_time_millis,json=parseTimeMillis,proto3" json:"parse_time_millis,omitempty"` + Label string `protobuf:"bytes,1,opt,name=label,proto3" json:"label,omitempty"` + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` + Files []*File `protobuf:"bytes,3,rep,name=files,proto3" json:"files,omitempty"` + Sha256 string `protobuf:"bytes,4,opt,name=sha256,proto3" json:"sha256,omitempty"` + ParseTimeMillis int64 `protobuf:"varint,5,opt,name=parse_time_millis,json=parseTimeMillis,proto3" json:"parse_time_millis,omitempty"` + ResolvedImports []*Import `protobuf:"bytes,6,rep,name=resolved_imports,json=resolvedImports,proto3" json:"resolved_imports,omitempty"` + ResolvedExports []*Import `protobuf:"bytes,7,rep,name=resolved_exports,json=resolvedExports,proto3" json:"resolved_exports,omitempty"` } func (x *Rule) Reset() { @@ -99,6 +101,20 @@ func (x *Rule) GetParseTimeMillis() int64 { return 0 } +func (x *Rule) GetResolvedImports() []*Import { + if x != nil { + return x.ResolvedImports + } + return nil +} + +func (x *Rule) GetResolvedExports() []*Import { + if x != nil { + return x.ResolvedExports + } + return nil +} + var File_build_stack_gazelle_scala_parse_rule_proto protoreflect.FileDescriptor var file_build_stack_gazelle_scala_parse_rule_proto_rawDesc = []byte{ @@ -109,7 +125,7 @@ var file_build_stack_gazelle_scala_parse_rule_proto_rawDesc = []byte{ 0x65, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x1a, 0x2a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x2f, 0x66, - 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb1, 0x01, 0x0a, 0x04, 0x52, 0x75, + 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd9, 0x02, 0x0a, 0x04, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x3b, 0x0a, 0x05, @@ -120,15 +136,25 @@ var file_build_stack_gazelle_scala_parse_rule_proto_rawDesc = []byte{ 0x32, 0x35, 0x36, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x42, 0x6a, 0x0a, - 0x1f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x67, 0x61, 0x7a, - 0x65, 0x6c, 0x6c, 0x65, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, - 0x50, 0x01, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, - 0x74, 0x61, 0x63, 0x6b, 0x62, 0x2f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2d, 0x67, 0x61, 0x7a, 0x65, - 0x6c, 0x6c, 0x65, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, - 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2f, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x3b, 0x70, 0x61, 0x72, 0x73, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x72, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x12, 0x52, 0x0a, + 0x10, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2e, 0x73, 0x63, + 0x61, 0x6c, 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x73, 0x12, 0x52, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x65, 0x78, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, + 0x65, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x49, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x45, 0x78, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x42, 0x6a, 0x0a, 0x1f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x73, + 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2e, 0x73, 0x63, 0x61, + 0x6c, 0x61, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x50, 0x01, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x62, 0x2f, 0x73, 0x63, + 0x61, 0x6c, 0x61, 0x2d, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2f, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x67, 0x61, 0x7a, 0x65, 0x6c, 0x6c, 0x65, 0x2f, + 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x3b, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -145,16 +171,19 @@ func file_build_stack_gazelle_scala_parse_rule_proto_rawDescGZIP() []byte { var file_build_stack_gazelle_scala_parse_rule_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_build_stack_gazelle_scala_parse_rule_proto_goTypes = []interface{}{ - (*Rule)(nil), // 0: build.stack.gazelle.scala.parse.Rule - (*File)(nil), // 1: build.stack.gazelle.scala.parse.File + (*Rule)(nil), // 0: build.stack.gazelle.scala.parse.Rule + (*File)(nil), // 1: build.stack.gazelle.scala.parse.File + (*Import)(nil), // 2: build.stack.gazelle.scala.parse.Import } var file_build_stack_gazelle_scala_parse_rule_proto_depIdxs = []int32{ 1, // 0: build.stack.gazelle.scala.parse.Rule.files:type_name -> build.stack.gazelle.scala.parse.File - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 2, // 1: build.stack.gazelle.scala.parse.Rule.resolved_imports:type_name -> build.stack.gazelle.scala.parse.Import + 2, // 2: build.stack.gazelle.scala.parse.Rule.resolved_exports:type_name -> build.stack.gazelle.scala.parse.Import + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_build_stack_gazelle_scala_parse_rule_proto_init() } diff --git a/build/stack/gazelle/scala/parse/rule.proto b/build/stack/gazelle/scala/parse/rule.proto index 99aa27c6..521cead7 100644 --- a/build/stack/gazelle/scala/parse/rule.proto +++ b/build/stack/gazelle/scala/parse/rule.proto @@ -20,5 +20,9 @@ message Rule { string sha256 = 4; // parse_time_millis is the time taken to collect filesnames and parse the files. int64 parse_time_millis = 5; + // resolved_imports is a list of imports that were resolved + repeated Import resolved_imports = 6; + // resolved_imports is a list of exports that were resolved + repeated Import resolved_exports = 7; } diff --git a/language/scala/BUILD.bazel b/language/scala/BUILD.bazel index 789c93e0..90216474 100644 --- a/language/scala/BUILD.bazel +++ b/language/scala/BUILD.bazel @@ -17,6 +17,7 @@ go_library( "flags.go", "generate.go", "kinds.go", + "known_file_registry.go", "known_rule_registry.go", "language.go", "lifecycle.go", diff --git a/language/scala/generate.go b/language/scala/generate.go index 9bd13035..1d57c33e 100644 --- a/language/scala/generate.go +++ b/language/scala/generate.go @@ -17,6 +17,7 @@ func (sl *scalaLang) GenerateRules(args language.GenerateArgs) language.Generate if args.File == nil { return language.GenerateResult{} } + sl.PutKnownFile(args.Rel, args.File) t1 := time.Now() diff --git a/language/scala/known_file_registry.go b/language/scala/known_file_registry.go new file mode 100644 index 00000000..8e891be4 --- /dev/null +++ b/language/scala/known_file_registry.go @@ -0,0 +1,42 @@ +package scala + +import ( + "fmt" + "os" + + "github.com/bazelbuild/bazel-gazelle/rule" +) + +// GetKnownFile implements part of the resolver.KnownFileRegistry interface. +func (sl *scalaLang) GetKnownFile(pkg string) (*rule.File, bool) { + r, ok := sl.knownFiles[pkg] + return r, ok +} + +// PutKnownFile implements part of the resolver.KnownFileRegistry interface. +func (sl *scalaLang) PutKnownFile(pkg string, r *rule.File) error { + if _, ok := sl.knownFiles[pkg]; ok { + return fmt.Errorf("duplicate known file: %s", pkg) + } + sl.knownFiles[pkg] = r + return nil +} + +func (sl *scalaLang) emitKnownFiles() error { + for pkg, file := range sl.knownFiles { + if err := sl.emitKnownFile(pkg, file); err != nil { + return err + } + } + if false { + return fmt.Errorf("known files: WIP") + } + return nil +} + +func (sl *scalaLang) emitKnownFile(_ string, file *rule.File) error { + if err := os.WriteFile(file.Path, file.Format(), 0o666); err != nil { + return err + } + return nil +} diff --git a/language/scala/language.go b/language/scala/language.go index 045a0870..601973df 100644 --- a/language/scala/language.go +++ b/language/scala/language.go @@ -69,6 +69,8 @@ type scalaLang struct { progress mobyprogress.Output // knownRules is a map of all known generated rules knownRules map[label.Label]*rule.Rule + // knownFiles is map of the parent File for knownRules + knownFiles map[string]*rule.File // conflictResolvers is a map of all known generated rules conflictResolvers map[string]resolver.ConflictResolver // globalScope includes all known symbols in the universe (minus package @@ -114,6 +116,7 @@ func NewLanguage() language.Language { globalScope: resolver.NewTrieScope(), globalPackages: resolver.NewTrieScope(), knownRules: make(map[label.Label]*rule.Rule), + knownFiles: make(map[string]*rule.File), conflictResolvers: make(map[string]resolver.ConflictResolver), packages: packages, progress: mobyprogress.NewProgressOutput(mobyprogress.NewOut(os.Stderr)), diff --git a/language/scala/lifecycle.go b/language/scala/lifecycle.go index 1cc20b00..9fb5082e 100644 --- a/language/scala/lifecycle.go +++ b/language/scala/lifecycle.go @@ -21,12 +21,6 @@ func (sl *scalaLang) onResolve() { } else { sl.globalScope = scalaScope } - - if sl.cacheFileFlagValue != "" { - if err := sl.writeScalaRuleCacheFile(); err != nil { - log.Fatalf("failed to write cache: %v", err) - } - } } // onEnd is called when the last rule has been resolved. @@ -37,6 +31,18 @@ func (sl *scalaLang) onEnd() { } } + if false { + if err := sl.emitKnownFiles(); err != nil { + log.Fatalf("failed to write known files: %v", err) + } + } + + if sl.cacheFileFlagValue != "" { + if err := sl.writeScalaRuleCacheFile(); err != nil { + log.Fatalf("failed to write cache: %v", err) + } + } + sl.stopCpuProfiling() sl.stopMemoryProfiling() } diff --git a/language/scala/scala_rule.go b/language/scala/scala_rule.go index 40cb0243..09b9ff47 100644 --- a/language/scala/scala_rule.go +++ b/language/scala/scala_rule.go @@ -135,6 +135,8 @@ func (r *scalaRule) ResolveImports(rctx *scalarule.ResolveContext) resolver.Impo if resolved != nil { item.imp.Symbol = resolved } else { + log.Println("deleting import!", item.imp.Imp) + delete(imports, item.imp.Imp) continue // skip this item if conflict strategy says "ok" but returns nil (it's a wildcard import) } } else { @@ -159,6 +161,8 @@ func (r *scalaRule) ResolveImports(rctx *scalarule.ResolveContext) resolver.Impo } } + r.pb.ResolvedImports = imports.ProtoList() + return imports } @@ -210,6 +214,8 @@ func (r *scalaRule) Exports() resolver.ImportMap { r.fileExports(file, exports) } + r.pb.ResolvedImports = exports.ProtoList() + return exports } diff --git a/pkg/resolver/BUILD.bazel b/pkg/resolver/BUILD.bazel index c1b92ea6..bc993a30 100644 --- a/pkg/resolver/BUILD.bazel +++ b/pkg/resolver/BUILD.bazel @@ -12,6 +12,7 @@ go_library( "global_symbol_provider_registry.go", "import.go", "import_map.go", + "known_file_registry.go", "known_rule_registry.go", "label_name_rewrite_spec.go", "memo_symbol_resolver.go", @@ -73,6 +74,7 @@ go_test( "@bazel_gazelle//resolve:go_default_library", "@bazel_gazelle//rule:go_default_library", "@com_github_google_go_cmp//cmp", + "@com_github_google_go_cmp//cmp/cmpopts", "@com_github_stretchr_testify//mock", ], ) diff --git a/pkg/resolver/import.go b/pkg/resolver/import.go index b8329533..572d6787 100644 --- a/pkg/resolver/import.go +++ b/pkg/resolver/import.go @@ -146,6 +146,22 @@ func (imp *Import) String() string { return strings.Join(parts, " ") } +func (imp *Import) Proto() *sppb.Import { + pb := &sppb.Import{ + Kind: imp.Kind, + Imp: imp.Imp, + Src: imp.Src, + Symbol: nil, + } + if imp.Source != nil { + pb.SourceFile = imp.Source.Filename + } + if imp.Symbol != nil { + pb.Symbol = imp.Symbol.Proto() + } + return pb +} + func IsSelfImport(imp *Import, repo, pkg, name string) bool { if imp.Symbol == nil { return false diff --git a/pkg/resolver/import_map.go b/pkg/resolver/import_map.go index 651276a6..440a4119 100644 --- a/pkg/resolver/import_map.go +++ b/pkg/resolver/import_map.go @@ -5,6 +5,7 @@ import ( "github.com/bazelbuild/bazel-gazelle/label" "github.com/bazelbuild/buildtools/build" + sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" ) // ImportMap is a map if imports keyed by the import string. @@ -93,3 +94,11 @@ func (imports ImportMap) Annotate(comments *build.Comments, accept func(imp *Imp comments.Before = append(comments.Before, build.Comment{Token: "# " + imp.String()}) } } + +func (imports ImportMap) ProtoList() []*sppb.Import { + list := make([]*sppb.Import, len(imports)) + for i, imp := range imports.Values() { + list[i] = imp.Proto() + } + return list +} diff --git a/pkg/resolver/known_file_registry.go b/pkg/resolver/known_file_registry.go new file mode 100644 index 00000000..cb616ef5 --- /dev/null +++ b/pkg/resolver/known_file_registry.go @@ -0,0 +1,16 @@ +package resolver + +import ( + "github.com/bazelbuild/bazel-gazelle/rule" +) + +// KnownFileRegistry is an index of known files keyed by their package name. +type KnownFileRegistry interface { + // GetKnownFile does a lookup of the given label and returns the + // known file. If not known `(nil, false)` is returned. + GetKnownFile(pkg string) (*rule.File, bool) + + // PutKnownFile adds the given known rule to the registry. It is an + // error to attempt duplicate registration of the same file twice. + PutKnownFile(pkg string, r *rule.File) error +} diff --git a/pkg/resolver/symbol.go b/pkg/resolver/symbol.go index 5bc96be1..00151ab0 100644 --- a/pkg/resolver/symbol.go +++ b/pkg/resolver/symbol.go @@ -62,6 +62,12 @@ func (s *Symbol) String() string { return fmt.Sprintf("(%s<%v> %s<%v>)", s.Name, s.Type, s.Label, s.Provider) } +func (s *Symbol) Proto() *sppb.Symbol { + return &sppb.Symbol{ + Name: s.Name, + } +} + func SymbolConfictMessage(symbol *Symbol, imp *Import, from label.Label) string { if len(symbol.Conflicts) == 0 { return "" From 05734da99c952c93c4ab9dc6dde67d7b6f416fde Mon Sep 17 00:00:00 2001 From: Paul Johnston Date: Mon, 24 Jun 2024 13:55:27 -0600 Subject: [PATCH 3/5] checkpoint before moving scalaconfig --- language/scala/language.go | 2 +- language/scala/scala_package.go | 2 +- language/scala/scala_rule.go | 11 ++--- pkg/parser/BUILD.bazel | 3 ++ pkg/parser/memo_parser.go | 5 +- pkg/parser/parser.go | 3 +- .../wildcard_import_expanding_parser.go | 47 +++++++++++++++++++ pkg/provider/source_provider.go | 2 +- pkg/provider/source_provider_test.go | 3 +- pkg/resolver/import.go | 7 +++ 10 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 pkg/parser/wildcard_import_expanding_parser.go diff --git a/language/scala/language.go b/language/scala/language.go index 601973df..936f5a57 100644 --- a/language/scala/language.go +++ b/language/scala/language.go @@ -128,7 +128,7 @@ func NewLanguage() language.Language { writeParseProgress(lang.progress, msg) } }) - lang.parser = parser.NewMemoParser(lang.sourceProvider) + lang.parser = parser.NewMemoParser(parser.NewWildcardExpandingParser(lang.sourceProvider)) lang.AddSymbolProvider(lang.sourceProvider) lang.AddSymbolProvider(provider.NewJavaProvider()) diff --git a/language/scala/scala_package.go b/language/scala/scala_package.go index 7fe05d0b..681b641c 100644 --- a/language/scala/scala_package.go +++ b/language/scala/scala_package.go @@ -204,7 +204,7 @@ func (s *scalaPackage) ParseRule(r *rule.Rule, attrName string) (scalarule.Rule, from := s.cfg.maybeRewrite(r.Kind(), label.Label{Pkg: s.rel, Name: r.Name()}) - rule, err := s.parser.ParseScalaRule(r.Kind(), from, dir, srcs...) + rule, err := s.parser.ParseScalaRule(s.cfg.config, r.Kind(), from, dir, srcs...) if err != nil { return nil, err } diff --git a/language/scala/scala_rule.go b/language/scala/scala_rule.go index 09b9ff47..b3d5a992 100644 --- a/language/scala/scala_rule.go +++ b/language/scala/scala_rule.go @@ -152,6 +152,8 @@ func (r *scalaRule) ResolveImports(rctx *scalarule.ResolveContext) resolver.Impo } } + // do something here to augment requires? + for _, req := range item.sym.Requires { if _, ok := imports[req.Name]; ok { continue @@ -293,7 +295,7 @@ func (r *scalaRule) fileImports(imports resolver.ImportMap, file *sppb.File) { // gather direct imports and import scopes for _, name := range file.Imports { - if wimp, ok := isWildcardImport(name); ok { + if wimp, ok := resolver.IsWildcardImport(name); ok { // collect the (package) symbol for import if sym, ok := r.ctx.scope.GetSymbol(name); ok { putImport(resolver.NewResolvedNameImport(sym.Name, file, name, sym)) @@ -437,13 +439,6 @@ func (r *scalaRule) putExport(imp string) { r.exports[imp] = resolve.ImportSpec{Imp: imp, Lang: scalaLangName} } -func isWildcardImport(imp string) (string, bool) { - if !strings.HasSuffix(imp, "._") { - return "", false - } - return imp[:len(imp)-len("._")], true -} - func isBinaryRule(kind string) bool { return strings.Contains(kind, "binary") || strings.Contains(kind, "test") } diff --git a/pkg/parser/BUILD.bazel b/pkg/parser/BUILD.bazel index 92cbaf19..e9ef4ec5 100644 --- a/pkg/parser/BUILD.bazel +++ b/pkg/parser/BUILD.bazel @@ -8,6 +8,7 @@ go_library( "memo_parser.go", "parser.go", "scalameta_parser.go", + "wildcard_import_expanding_parser.go", ], embedsrcs = [ "node_modules/scalameta-parsers/index.js", @@ -19,6 +20,8 @@ go_library( deps = [ "//build/stack/gazelle/scala/parse", "//pkg/collections", + "//pkg/resolver", + "@bazel_gazelle//config:go_default_library", "@bazel_gazelle//label:go_default_library", "@com_github_amenzhinsky_go_memexec//:go-memexec", "@io_bazel_rules_go//go/tools/bazel:go_default_library", diff --git a/pkg/parser/memo_parser.go b/pkg/parser/memo_parser.go index 6b15fb03..a39976ef 100644 --- a/pkg/parser/memo_parser.go +++ b/pkg/parser/memo_parser.go @@ -7,6 +7,7 @@ import ( "path/filepath" "sort" + "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/label" sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" "github.com/stackb/scala-gazelle/pkg/collections" @@ -29,7 +30,7 @@ func NewMemoParser(next Parser) *MemoParser { } // ParseScalaRule implements parser.Parser -func (p *MemoParser) ParseScalaRule(kind string, from label.Label, dir string, srcs ...string) (*sppb.Rule, error) { +func (p *MemoParser) ParseScalaRule(c *config.Config, kind string, from label.Label, dir string, srcs ...string) (*sppb.Rule, error) { sort.Strings(srcs) var hash bytes.Buffer @@ -62,7 +63,7 @@ func (p *MemoParser) ParseScalaRule(kind string, from label.Label, dir string, s log.Panicf(`while parsing %s %s: no files to parse! (this is a bug)`, kind, from) } - rule, err := p.next.ParseScalaRule(kind, from, dir, srcs...) + rule, err := p.next.ParseScalaRule(c, kind, from, dir, srcs...) if err != nil { return nil, err } diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index c7224b64..0a12c2f4 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -1,6 +1,7 @@ package parser import ( + "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/label" sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" ) @@ -13,5 +14,5 @@ type Parser interface { // ParseScalaRule is used to parse a list of source files. The srcs list // is expected to be relative to dir. - ParseScalaRule(kind string, from label.Label, dir string, srcs ...string) (*sppb.Rule, error) + ParseScalaRule(cfg *config.Config, kind string, from label.Label, dir string, srcs ...string) (*sppb.Rule, error) } diff --git a/pkg/parser/wildcard_import_expanding_parser.go b/pkg/parser/wildcard_import_expanding_parser.go new file mode 100644 index 00000000..085adfc2 --- /dev/null +++ b/pkg/parser/wildcard_import_expanding_parser.go @@ -0,0 +1,47 @@ +package parser + +import ( + "log" + + "github.com/bazelbuild/bazel-gazelle/config" + "github.com/bazelbuild/bazel-gazelle/label" + sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" + "github.com/stackb/scala-gazelle/pkg/resolver" +) + +const debugWildcardImportExpandingParser = false + +// WildcardImportExpandingParser is a Parser frontend that removes wildcard imports. +type WildcardImportExpandingParser struct { + next Parser + rules map[label.Label]*sppb.Rule +} + +func NewWildcardExpandingParser(next Parser) *WildcardImportExpandingParser { + return &WildcardImportExpandingParser{ + next: next, + rules: make(map[label.Label]*sppb.Rule), + } +} + +// ParseScalaRule implements parser.Parser +func (p *WildcardImportExpandingParser) ParseScalaRule(c *config.Config, kind string, from label.Label, dir string, srcs ...string) (*sppb.Rule, error) { + rule, err := p.next.ParseScalaRule(c, kind, from, dir, srcs...) + if err != nil { + return nil, err + } + + for _, file := range rule.Files { + for _, imp := range file.Imports { + if _, ok := resolver.IsWildcardImport(imp); ok { + log.Printf("%s: wildcard import: %s", file.Filename, imp) + } + } + } + + return rule, nil +} + +func (p *WildcardImportExpandingParser) LoadScalaRule(from label.Label, rule *sppb.Rule) error { + return p.next.LoadScalaRule(from, rule) +} diff --git a/pkg/provider/source_provider.go b/pkg/provider/source_provider.go index 00d12f1b..24d8d426 100644 --- a/pkg/provider/source_provider.go +++ b/pkg/provider/source_provider.go @@ -85,7 +85,7 @@ func (r *SourceProvider) start() error { } // ParseScalaRule implements scalarule.Parser -func (r *SourceProvider) ParseScalaRule(kind string, from label.Label, dir string, srcs ...string) (*sppb.Rule, error) { +func (r *SourceProvider) ParseScalaRule(c *config.Config, kind string, from label.Label, dir string, srcs ...string) (*sppb.Rule, error) { if len(srcs) == 0 { return nil, nil } diff --git a/pkg/provider/source_provider_test.go b/pkg/provider/source_provider_test.go index 4ae873e8..87bcb16e 100644 --- a/pkg/provider/source_provider_test.go +++ b/pkg/provider/source_provider_test.go @@ -22,6 +22,7 @@ import ( var update = flag.Bool("update", false, "update golden files") func TestScalaSourceProviderParseScalaRule(t *testing.T) { + rel := "pkg/provider" dir, err := os.Getwd() if err != nil { @@ -63,7 +64,7 @@ func TestScalaSourceProviderParseScalaRule(t *testing.T) { t.Run(src, func(t *testing.T) { goldenFile := filepath.Join(dir, src+".golden.json") from := label.Label{Pkg: rel, Name: src} - got, err := provider.ParseScalaRule("scala_library", from, dir, src) + got, err := provider.ParseScalaRule(c, "scala_library", from, dir, src) if err != nil { t.Fatal(err) } diff --git a/pkg/resolver/import.go b/pkg/resolver/import.go index 572d6787..f0fbfc47 100644 --- a/pkg/resolver/import.go +++ b/pkg/resolver/import.go @@ -177,3 +177,10 @@ func IsSelfImport(imp *Import, repo, pkg, name string) bool { } return true } + +func IsWildcardImport(imp string) (string, bool) { + if !strings.HasSuffix(imp, "._") { + return "", false + } + return imp[:len(imp)-len("._")], true +} From 79be313ea6790f98800b1ac8adf5d98d92a552c8 Mon Sep 17 00:00:00 2001 From: Paul Johnston Date: Mon, 24 Jun 2024 14:13:21 -0600 Subject: [PATCH 4/5] migrate scalaconfig to own package --- language/scala/BUILD.bazel | 6 +- language/scala/configure.go | 3 +- language/scala/existing_scala_rule.go | 19 +-- language/scala/existing_scala_rule_test.go | 73 ----------- language/scala/generate.go | 3 +- language/scala/language.go | 11 +- language/scala/scala_package.go | 15 +-- language/scala/scala_package_test.go | 3 +- language/scala/scala_rule.go | 17 ++- language/scala/scala_rule_test.go | 5 +- pkg/scalaconfig/BUILD.bazel | 41 +++++++ .../scalaconfig/config.go | 113 +++++++++++------- .../scalaconfig/config_test.go | 98 +++++++++++---- pkg/scalaconfig/test_config.go | 16 +++ 14 files changed, 242 insertions(+), 181 deletions(-) create mode 100644 pkg/scalaconfig/BUILD.bazel rename language/scala/scala_config.go => pkg/scalaconfig/config.go (79%) rename language/scala/scala_config_test.go => pkg/scalaconfig/config_test.go (86%) create mode 100644 pkg/scalaconfig/test_config.go diff --git a/language/scala/BUILD.bazel b/language/scala/BUILD.bazel index 90216474..696f916e 100644 --- a/language/scala/BUILD.bazel +++ b/language/scala/BUILD.bazel @@ -25,7 +25,6 @@ go_library( "package_marker_rule.go", "progress.go", "resolve.go", - "scala_config.go", "scala_package.go", "scala_rule.go", "scope.go", @@ -43,6 +42,7 @@ go_library( "//pkg/protobuf", "//pkg/provider", "//pkg/resolver", + "//pkg/scalaconfig", "//pkg/scalarule", "@bazel_gazelle//config:go_default_library", "@bazel_gazelle//label:go_default_library", @@ -52,7 +52,6 @@ go_library( "@bazel_gazelle//rule:go_default_library", "@build_stack_rules_proto//pkg/protoc", "@com_github_bazelbuild_buildtools//build:go_default_library", - "@com_github_bmatcuk_doublestar_v4//:doublestar", "@com_github_pcj_mobyprogress//:mobyprogress", ], ) @@ -84,7 +83,6 @@ go_test( "golden_test.go", "language_test.go", "loads_test.go", - "scala_config_test.go", "scala_package_test.go", "scala_rule_test.go", ], @@ -94,6 +92,7 @@ go_test( "//build/stack/gazelle/scala/parse", "//pkg/resolver", "//pkg/resolver/mocks", + "//pkg/scalaconfig", "//pkg/scalarule", "//pkg/testutil", "@bazel_gazelle//config:go_default_library", @@ -103,7 +102,6 @@ go_test( "@bazel_gazelle//testtools:go_default_library", "@build_stack_rules_proto//pkg/goldentest", "@com_github_google_go_cmp//cmp", - "@com_github_google_go_cmp//cmp/cmpopts", "@com_github_stretchr_testify//mock", ], ) diff --git a/language/scala/configure.go b/language/scala/configure.go index 0cc5f982..29c0abfb 100644 --- a/language/scala/configure.go +++ b/language/scala/configure.go @@ -5,12 +5,13 @@ import ( "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/rule" + "github.com/stackb/scala-gazelle/pkg/scalaconfig" ) // Configure implements part of the language.Language interface func (sl *scalaLang) Configure(c *config.Config, rel string, f *rule.File) { if f != nil { - if err := getOrCreateScalaConfig(sl, c, rel).parseDirectives(f.Directives); err != nil { + if err := scalaconfig.GetOrCreate(sl, c, rel).ParseDirectives(f.Directives); err != nil { log.Fatalf("parsing directives in package %q: %v", rel, err) } } diff --git a/language/scala/existing_scala_rule.go b/language/scala/existing_scala_rule.go index 2d4cc96d..2ca6484a 100644 --- a/language/scala/existing_scala_rule.go +++ b/language/scala/existing_scala_rule.go @@ -10,6 +10,7 @@ import ( "github.com/bazelbuild/buildtools/build" "github.com/stackb/scala-gazelle/pkg/resolver" + "github.com/stackb/scala-gazelle/pkg/scalaconfig" "github.com/stackb/scala-gazelle/pkg/scalarule" ) @@ -127,20 +128,20 @@ func (s *existingScalaRule) Resolve(rctx *scalarule.ResolveContext, importsRaw i exports := scalaRule.ResolveExports(rctx) r := rctx.Rule - sc := getScalaConfig(rctx.Config) + sc := scalaconfig.Get(rctx.Config) // part 1a: deps - newImports := imports.Deps(sc.maybeRewrite(r.Kind(), rctx.From)) - depLabels := sc.cleanDeps(rctx.From, r.Attr("deps"), newImports) - mergeDeps(r.Kind(), depLabels, newImports) + newImports := imports.Deps(sc.MaybeRewrite(r.Kind(), rctx.From)) + depLabels := sc.CleanDeps(rctx.From, r.Attr("deps"), newImports) + scalaconfig.MergeDeps(r.Kind(), depLabels, newImports) if len(depLabels.List) > 0 { r.SetAttr("deps", depLabels) } else { r.DelAttr("deps") } - if sc.shouldAnnotateImports() { + if sc.ShouldAnnotateImports() { comments := r.AttrComments("srcs") if comments != nil { annotateImports(imports, comments, "import: ") @@ -149,16 +150,16 @@ func (s *existingScalaRule) Resolve(rctx *scalarule.ResolveContext, importsRaw i // part 1b: exports if s.isLibrary { - newExports := exports.Deps(sc.maybeRewrite(r.Kind(), rctx.From)) - exportLabels := sc.cleanExports(rctx.From, r.Attr("exports"), newExports) - mergeDeps(r.Kind(), exportLabels, newExports) + newExports := exports.Deps(sc.MaybeRewrite(r.Kind(), rctx.From)) + exportLabels := sc.CleanExports(rctx.From, r.Attr("exports"), newExports) + scalaconfig.MergeDeps(r.Kind(), exportLabels, newExports) if len(exportLabels.List) > 0 { r.SetAttr("exports", exportLabels) } else { r.DelAttr("exports") } - if sc.shouldAnnotateExports() { + if sc.ShouldAnnotateExports() { comments := r.AttrComments("srcs") if comments != nil { annotateImports(exports, comments, "export: ") diff --git a/language/scala/existing_scala_rule_test.go b/language/scala/existing_scala_rule_test.go index b88b8575..c7c8e9fe 100644 --- a/language/scala/existing_scala_rule_test.go +++ b/language/scala/existing_scala_rule_test.go @@ -1,74 +1 @@ package scala - -import ( - "testing" - - "github.com/bazelbuild/bazel-gazelle/label" - "github.com/bazelbuild/bazel-gazelle/rule" - "github.com/google/go-cmp/cmp" -) - -func TestScalaDepLabel(t *testing.T) { - for name, tc := range map[string]struct { - in string - want label.Label - }{ - "degenerate": { - in: ` -test( - expr = "", -) - `, - want: label.NoLabel, - }, - "invalid label": { - in: ` -test( - expr = "@@@", -) - `, - want: label.NoLabel, - }, - "valid label": { - in: ` -test( - expr = "@foo//bar:baz", -) - `, - want: label.New("foo", "bar", "baz"), - }, - "invalid callexpr": { - in: ` -test( - expr = fn("@foo//bar:baz"), -) - `, - want: label.NoLabel, - }, - "valid callexpr": { - in: ` -test( - expr = scala_dep("@foo//bar:baz"), -) - `, - want: label.New("foo", "bar", "baz"), - }, - } { - t.Run(name, func(t *testing.T) { - file, err := rule.LoadData("", "BUILD", []byte(tc.in)) - if err != nil { - t.Fatal(err) - } - if len(file.Rules) != 1 { - t.Fatalf("expected single in rule, got %d", len(file.Rules)) - } - target := file.Rules[0] - expr := target.Attr("expr") - got := labelFromDepExpr(expr) - - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("label (-want +got):\n%s", diff) - } - }) - } -} diff --git a/language/scala/generate.go b/language/scala/generate.go index 1d57c33e..579531a3 100644 --- a/language/scala/generate.go +++ b/language/scala/generate.go @@ -7,6 +7,7 @@ import ( "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/label" "github.com/bazelbuild/bazel-gazelle/language" + "github.com/stackb/scala-gazelle/pkg/scalaconfig" ) const debugGenerate = false @@ -25,7 +26,7 @@ func (sl *scalaLang) GenerateRules(args language.GenerateArgs) language.Generate writeGenerateProgress(sl.progress, len(sl.packages), int(sl.cache.PackageCount)) } - sc := getScalaConfig(args.Config) + sc := scalaconfig.Get(args.Config) pkg := newScalaPackage(args.Rel, args.File, sc, sl.ruleProviderRegistry, sl.parser, sl) sl.packages[args.Rel] = pkg sl.remainingPackages++ diff --git a/language/scala/language.go b/language/scala/language.go index 936f5a57..6ca2875c 100644 --- a/language/scala/language.go +++ b/language/scala/language.go @@ -15,6 +15,7 @@ import ( "github.com/stackb/scala-gazelle/pkg/parser" "github.com/stackb/scala-gazelle/pkg/provider" "github.com/stackb/scala-gazelle/pkg/resolver" + "github.com/stackb/scala-gazelle/pkg/scalaconfig" "github.com/stackb/scala-gazelle/pkg/scalarule" ) @@ -94,15 +95,7 @@ func (sl *scalaLang) Name() string { return scalaLangName } // KnownDirectives implements part of the language.Language interface func (*scalaLang) KnownDirectives() []string { - return []string{ - resolveConflictsDirective, - resolveFileSymbolName, - resolveGlobDirective, - resolveKindRewriteNameDirective, - resolveWithDirective, - scalaDebugDirective, - scalaRuleDirective, - } + return scalaconfig.DirectiveNames() } // NewLanguage is called by Gazelle to install this language extension in a diff --git a/language/scala/scala_package.go b/language/scala/scala_package.go index 681b641c..033c5f3b 100644 --- a/language/scala/scala_package.go +++ b/language/scala/scala_package.go @@ -14,6 +14,7 @@ import ( "github.com/stackb/scala-gazelle/pkg/glob" "github.com/stackb/scala-gazelle/pkg/parser" "github.com/stackb/scala-gazelle/pkg/resolver" + "github.com/stackb/scala-gazelle/pkg/scalaconfig" "github.com/stackb/scala-gazelle/pkg/scalarule" ) @@ -36,7 +37,7 @@ type scalaPackage struct { // the build file file *rule.File // the config for this package - cfg *scalaConfig + cfg *scalaconfig.Config // the generated and empty rule providers gen, empty []scalarule.RuleProvider // rules is the final state of generated rules, by name. @@ -50,7 +51,7 @@ type scalaPackage struct { func newScalaPackage( rel string, file *rule.File, - cfg *scalaConfig, + cfg *scalaconfig.Config, providerRegistry scalarule.ProviderRegistry, parser parser.Parser, universe resolver.Universe) *scalaPackage { @@ -71,7 +72,7 @@ func newScalaPackage( } // Config returns the the underlying config. -func (s *scalaPackage) Config() *scalaConfig { +func (s *scalaPackage) Config() *scalaconfig.Config { return s.cfg } @@ -129,7 +130,7 @@ func (s *scalaPackage) generateRules(enabled bool) []scalarule.RuleProvider { } } - configuredRules := s.cfg.configuredRules() + configuredRules := s.cfg.ConfiguredRules() for _, rc := range configuredRules { // if enabled != rc.Enabled { @@ -202,9 +203,9 @@ func (s *scalaPackage) ParseRule(r *rule.Rule, attrName string) (scalarule.Rule, return nil, ErrRuleHasNoSrcs } - from := s.cfg.maybeRewrite(r.Kind(), label.Label{Pkg: s.rel, Name: r.Name()}) + from := s.cfg.MaybeRewrite(r.Kind(), label.Label{Pkg: s.rel, Name: r.Name()}) - rule, err := s.parser.ParseScalaRule(s.cfg.config, r.Kind(), from, dir, srcs...) + rule, err := s.parser.ParseScalaRule(s.cfg.Config(), r.Kind(), from, dir, srcs...) if err != nil { return nil, err } @@ -221,7 +222,7 @@ func (s *scalaPackage) ParseRule(r *rule.Rule, attrName string) (scalarule.Rule, // repoRootDir return the root directory of the repo. func (s *scalaPackage) repoRootDir() string { - return s.cfg.config.RepoRoot + return s.cfg.Config().RepoRoot } // Rules provides the aggregated rule list for the package. diff --git a/language/scala/scala_package_test.go b/language/scala/scala_package_test.go index e26b7086..ef88b7f8 100644 --- a/language/scala/scala_package_test.go +++ b/language/scala/scala_package_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stackb/scala-gazelle/pkg/resolver/mocks" + "github.com/stackb/scala-gazelle/pkg/scalaconfig" "github.com/stackb/scala-gazelle/pkg/scalarule" ) @@ -35,7 +36,7 @@ func TestScalaPackageParseRule(t *testing.T) { Return(nil) c := config.New() - cfg := newScalaConfig(universe, c, "") + cfg := scalaconfig.New(universe, c, "") pkg := scalaPackage{ cfg: cfg, diff --git a/language/scala/scala_rule.go b/language/scala/scala_rule.go index b3d5a992..12bd4bbd 100644 --- a/language/scala/scala_rule.go +++ b/language/scala/scala_rule.go @@ -14,6 +14,7 @@ import ( sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" "github.com/stackb/scala-gazelle/pkg/collections" "github.com/stackb/scala-gazelle/pkg/resolver" + "github.com/stackb/scala-gazelle/pkg/scalaconfig" "github.com/stackb/scala-gazelle/pkg/scalarule" ) @@ -27,7 +28,7 @@ const ( type scalaRuleContext struct { // the parent config - scalaConfig *scalaConfig + scalaConfig *scalaconfig.Config // rule (lowercase) is the parent gazelle rule rule *rule.Rule // scope is a map of symbols that are outside the rule. @@ -100,7 +101,7 @@ func (r *scalaRule) ResolveExports(rctx *scalarule.ResolveContext) resolver.Impo // ResolveImports performs symbol resolution for imports of the rule. func (r *scalaRule) ResolveImports(rctx *scalarule.ResolveContext) resolver.ImportMap { imports := r.Imports() - sc := getScalaConfig(rctx.Config) + sc := scalaconfig.Get(rctx.Config) transitive := newImportSymbols() @@ -131,7 +132,7 @@ func (r *scalaRule) ResolveImports(rctx *scalarule.ResolveContext) resolver.Impo item, _ := transitive.Pop() if len(item.sym.Conflicts) > 0 { - if resolved, ok := sc.resolveConflict(rctx.Rule, imports, item.imp, item.sym, rctx.From); ok { + if resolved, ok := sc.ResolveConflict(rctx.Rule, imports, item.imp, item.sym, rctx.From); ok { if resolved != nil { item.imp.Symbol = resolved } else { @@ -140,12 +141,10 @@ func (r *scalaRule) ResolveImports(rctx *scalarule.ResolveContext) resolver.Impo continue // skip this item if conflict strategy says "ok" but returns nil (it's a wildcard import) } } else { - if r.ctx.scalaConfig.shouldAnnotateWildcardImports() && item.sym.Type == sppb.ImportType_PROTO_PACKAGE { + if r.ctx.scalaConfig.ShouldAnnotateWildcardImports() && item.sym.Type == sppb.ImportType_PROTO_PACKAGE { if scope, ok := r.ctx.scope.GetScope(item.imp.Imp); ok { wildcardImport := item.imp.Src // original symbol name having underscore suffix r.handleWildcardImport(item.imp.Source, wildcardImport, scope) - } else { - } } fmt.Println(resolver.SymbolConfictMessage(item.sym, item.imp, rctx.From)) @@ -199,7 +198,7 @@ func (r *scalaRule) Imports() resolver.ImportMap { // Gather implicit imports transitively. for !required.IsEmpty() { src, _ := required.Pop() - for _, dst := range r.ctx.scalaConfig.getImplicitImports(impLang, src) { + for _, dst := range r.ctx.scalaConfig.GetImplicitImports(impLang, src) { required.Push(dst) imports.Put(resolver.NewImplicitImport(dst, src)) } @@ -227,7 +226,7 @@ func (r *scalaRule) fileExports(file *sppb.File, exports resolver.ImportMap) { direct := resolver.NewTrieScope() putExport := func(imp *resolver.Import) { - if resolver.IsSelfImport(imp, "", r.ctx.scalaConfig.rel, r.ctx.rule.Name()) { + if resolver.IsSelfImport(imp, "", r.ctx.scalaConfig.Rel(), r.ctx.rule.Name()) { if debugSelfImports { log.Println("skipping export from current", imp.Imp) } @@ -284,7 +283,7 @@ func (r *scalaRule) fileImports(imports resolver.ImportMap, file *sppb.File) { direct := resolver.NewTrieScope() putImport := func(imp *resolver.Import) { - if resolver.IsSelfImport(imp, "", r.ctx.scalaConfig.rel, r.ctx.rule.Name()) { + if resolver.IsSelfImport(imp, "", r.ctx.scalaConfig.Rel(), r.ctx.rule.Name()) { if debugSelfImports { log.Println("skipping import from current", imp.Imp) } diff --git a/language/scala/scala_rule_test.go b/language/scala/scala_rule_test.go index b2e6f742..cf5db9aa 100644 --- a/language/scala/scala_rule_test.go +++ b/language/scala/scala_rule_test.go @@ -14,6 +14,7 @@ import ( sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" "github.com/stackb/scala-gazelle/pkg/resolver" "github.com/stackb/scala-gazelle/pkg/resolver/mocks" + "github.com/stackb/scala-gazelle/pkg/scalaconfig" ) func TestScalaRuleExports(t *testing.T) { @@ -70,7 +71,7 @@ func TestScalaRuleExports(t *testing.T) { Return(nil) c := config.New() - sc := newScalaConfig(universe, c, "") + sc := scalaconfig.New(universe, c, "") ctx := &scalaRuleContext{ rule: tc.rule, @@ -261,7 +262,7 @@ func TestScalaRuleImports(t *testing.T) { global.PutSymbol(symbol) } - sc, err := newTestScalaConfig(t, universe, tc.from.Pkg, makeDirectives(tc.directives)...) + sc, err := scalaconfig.NewTestScalaConfig(t, universe, tc.from.Pkg, makeDirectives(tc.directives)...) if err != nil { t.Fatal(err) } diff --git a/pkg/scalaconfig/BUILD.bazel b/pkg/scalaconfig/BUILD.bazel new file mode 100644 index 00000000..4f24d800 --- /dev/null +++ b/pkg/scalaconfig/BUILD.bazel @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "scalaconfig", + srcs = [ + "config.go", + "test_config.go", + ], + importpath = "github.com/stackb/scala-gazelle/pkg/scalaconfig", + visibility = ["//visibility:public"], + deps = [ + "//pkg/collections", + "//pkg/resolver", + "//pkg/scalarule", + "@bazel_gazelle//config:go_default_library", + "@bazel_gazelle//label:go_default_library", + "@bazel_gazelle//resolve:go_default_library", + "@bazel_gazelle//rule:go_default_library", + "@com_github_bazelbuild_buildtools//build:go_default_library", + "@com_github_bmatcuk_doublestar_v4//:doublestar", + ], +) + +go_test( + name = "scalaconfig_test", + srcs = ["config_test.go"], + embed = [":scalaconfig"], + deps = [ + "//pkg/resolver", + "//pkg/resolver/mocks", + "//pkg/scalarule", + "//pkg/testutil", + "@bazel_gazelle//config:go_default_library", + "@bazel_gazelle//label:go_default_library", + "@bazel_gazelle//resolve:go_default_library", + "@bazel_gazelle//rule:go_default_library", + "@com_github_google_go_cmp//cmp", + "@com_github_google_go_cmp//cmp/cmpopts", + "@com_github_stretchr_testify//mock", + ], +) diff --git a/language/scala/scala_config.go b/pkg/scalaconfig/config.go similarity index 79% rename from language/scala/scala_config.go rename to pkg/scalaconfig/config.go index ca0855c5..6ceab0c3 100644 --- a/language/scala/scala_config.go +++ b/pkg/scalaconfig/config.go @@ -1,4 +1,4 @@ -package scala +package scalaconfig import ( "fmt" @@ -25,6 +25,7 @@ const ( DebugImports debugAnnotation = 1 DebugExports debugAnnotation = 2 DebugWildcardImports debugAnnotation = 3 + scalaLangName = "scala" ) const ( @@ -37,8 +38,20 @@ const ( resolveKindRewriteNameDirective = "resolve_kind_rewrite_name" ) -// scalaConfig represents the config extension for the a scala package. -type scalaConfig struct { +func DirectiveNames() []string { + return []string{ + scalaDebugDirective, + scalaRuleDirective, + resolveGlobDirective, + resolveConflictsDirective, + resolveWithDirective, + resolveFileSymbolName, + resolveKindRewriteNameDirective, + } +} + +// Config represents the config extension for the a scala package. +type Config struct { config *config.Config rel string universe resolver.Universe @@ -51,9 +64,9 @@ type scalaConfig struct { conflictResolvers []resolver.ConflictResolver } -// newScalaConfig initializes a new scalaConfig. -func newScalaConfig(universe resolver.Universe, config *config.Config, rel string) *scalaConfig { - return &scalaConfig{ +// newScalaConfig initializes a new Config. +func New(universe resolver.Universe, config *config.Config, rel string) *Config { + return &Config{ config: config, rel: rel, universe: universe, @@ -63,10 +76,10 @@ func newScalaConfig(universe resolver.Universe, config *config.Config, rel strin } } -// getScalaConfig returns the scala config. Can be nil. -func getScalaConfig(config *config.Config) *scalaConfig { +// Get returns the scala config. Can be nil. +func Get(config *config.Config) *Config { if existingExt, ok := config.Exts[scalaLangName]; ok { - return existingExt.(*scalaConfig) + return existingExt.(*Config) } else { return nil } @@ -74,20 +87,20 @@ func getScalaConfig(config *config.Config) *scalaConfig { // getOrCreateScalaConfig either inserts a new config into the map under the // language name or replaces it with a clone. -func getOrCreateScalaConfig(universe resolver.Universe, config *config.Config, rel string) *scalaConfig { - var cfg *scalaConfig +func GetOrCreate(universe resolver.Universe, config *config.Config, rel string) *Config { + var cfg *Config if existingExt, ok := config.Exts[scalaLangName]; ok { - cfg = existingExt.(*scalaConfig).clone(config, rel) + cfg = existingExt.(*Config).clone(config, rel) } else { - cfg = newScalaConfig(universe, config, rel) + cfg = New(universe, config, rel) } config.Exts[scalaLangName] = cfg return cfg } // clone copies this config to a new one. -func (c *scalaConfig) clone(config *config.Config, rel string) *scalaConfig { - clone := newScalaConfig(c.universe, config, rel) +func (c *Config) clone(config *config.Config, rel string) *Config { + clone := New(c.universe, config, rel) for k, v := range c.annotations { clone.annotations[k] = v } @@ -112,7 +125,17 @@ func (c *scalaConfig) clone(config *config.Config, rel string) *scalaConfig { return clone } -func (c *scalaConfig) canProvide(from label.Label) bool { +// Config returns the parent gazelle configuration +func (c *Config) Config() *config.Config { + return c.config +} + +// Rel returns the parent gazelle relative path +func (c *Config) Rel() string { + return c.rel +} + +func (c *Config) CanProvide(from label.Label) bool { for _, provider := range c.universe.SymbolProviders() { if provider.CanProvide(from, c.universe.GetKnownRule) { return true @@ -121,7 +144,7 @@ func (c *scalaConfig) canProvide(from label.Label) bool { return false } -func (c *scalaConfig) resolveConflict(r *rule.Rule, imports resolver.ImportMap, imp *resolver.Import, symbol *resolver.Symbol, from label.Label) (*resolver.Symbol, bool) { +func (c *Config) ResolveConflict(r *rule.Rule, imports resolver.ImportMap, imp *resolver.Import, symbol *resolver.Symbol, from label.Label) (*resolver.Symbol, bool) { for _, resolver := range c.conflictResolvers { if resolved, ok := resolver.ResolveConflict(c.universe, r, imports, imp, symbol, from); ok { return resolved, true @@ -131,7 +154,7 @@ func (c *scalaConfig) resolveConflict(r *rule.Rule, imports resolver.ImportMap, } // GetKnownRule translates relative labels into their absolute form. -func (c *scalaConfig) GetKnownRule(from label.Label) (*rule.Rule, bool) { +func (c *Config) GetKnownRule(from label.Label) (*rule.Rule, bool) { if from.Name == "" { return nil, false } @@ -144,7 +167,7 @@ func (c *scalaConfig) GetKnownRule(from label.Label) (*rule.Rule, bool) { // parseDirectives is called in each directory visited by gazelle. The relative // directory name is given by 'rel' and the list of directives in the BUILD file // are specified by 'directives'. -func (c *scalaConfig) parseDirectives(directives []rule.Directive) (err error) { +func (c *Config) ParseDirectives(directives []rule.Directive) (err error) { for _, d := range directives { switch d.Key { case scalaRuleDirective: @@ -173,7 +196,7 @@ func (c *scalaConfig) parseDirectives(directives []rule.Directive) (err error) { return } -func (c *scalaConfig) parseScalaRuleDirective(d rule.Directive) error { +func (c *Config) parseScalaRuleDirective(d rule.Directive) error { fields := strings.Fields(d.Value) if len(fields) < 3 { return fmt.Errorf("expected three or more fields, got %d", len(fields)) @@ -186,7 +209,7 @@ func (c *scalaConfig) parseScalaRuleDirective(d rule.Directive) error { return r.ParseDirective(name, param, value) } -func (c *scalaConfig) parseResolveGlobDirective(d rule.Directive) { +func (c *Config) parseResolveGlobDirective(d rule.Directive) { parts := strings.Fields(d.Value) o := overrideSpec{} var lbl string @@ -211,7 +234,7 @@ func (c *scalaConfig) parseResolveGlobDirective(d rule.Directive) { c.overrides = append(c.overrides, &o) } -func (c *scalaConfig) parseResolveWithDirective(d rule.Directive) { +func (c *Config) parseResolveWithDirective(d rule.Directive) { parts := strings.Fields(d.Value) if len(parts) < 3 { log.Printf("invalid gazelle:%s directive: expected 3+ parts, got %d (%v)", resolveWithDirective, len(parts), parts) @@ -224,7 +247,7 @@ func (c *scalaConfig) parseResolveWithDirective(d rule.Directive) { }) } -func (c *scalaConfig) parseResolveFileSymbolNames(d rule.Directive) { +func (c *Config) parseResolveFileSymbolNames(d rule.Directive) { parts := strings.Fields(d.Value) if len(parts) < 2 { log.Printf("invalid gazelle:%s directive: expected [FILENAME_PATTERN [+|-]SYMBOLS...], got %v", resolveFileSymbolName, parts) @@ -241,7 +264,7 @@ func (c *scalaConfig) parseResolveFileSymbolNames(d rule.Directive) { } } -func (c *scalaConfig) parseResolveKindRewriteNameDirective(d rule.Directive) { +func (c *Config) parseResolveKindRewriteNameDirective(d rule.Directive) { parts := strings.Fields(d.Value) if len(parts) != 3 { log.Printf("invalid gazelle:%s directive: expected [KIND SRC_NAME DST_NAME], got %v", resolveKindRewriteNameDirective, parts) @@ -254,7 +277,7 @@ func (c *scalaConfig) parseResolveKindRewriteNameDirective(d rule.Directive) { c.labelNameRewrites[kind] = resolver.LabelNameRewriteSpec{Src: src, Dst: dst} } -func (c *scalaConfig) parseResolveConflictsDirective(d rule.Directive) error { +func (c *Config) parseResolveConflictsDirective(d rule.Directive) error { for _, key := range strings.Fields(d.Value) { intent := collections.ParseIntent(key) if intent.Want { @@ -279,7 +302,7 @@ func (c *scalaConfig) parseResolveConflictsDirective(d rule.Directive) error { return nil } -func (c *scalaConfig) parseScalaAnnotation(d rule.Directive) error { +func (c *Config) parseScalaAnnotation(d rule.Directive) error { for _, key := range strings.Fields(d.Value) { intent := collections.ParseIntent(key) annot := parseAnnotation(intent.Value) @@ -296,7 +319,7 @@ func (c *scalaConfig) parseScalaAnnotation(d rule.Directive) error { return nil } -func (c *scalaConfig) getOrCreateScalaRuleConfig(config *config.Config, name string) (*scalarule.Config, error) { +func (c *Config) getOrCreateScalaRuleConfig(config *config.Config, name string) (*scalarule.Config, error) { r, ok := c.rules[name] if !ok { r = scalarule.NewConfig(config, name) @@ -306,7 +329,7 @@ func (c *scalaConfig) getOrCreateScalaRuleConfig(config *config.Config, name str return r, nil } -func (c *scalaConfig) getImplicitImports(lang, imp string) (deps []string) { +func (c *Config) GetImplicitImports(lang, imp string) (deps []string) { for _, d := range c.implicitImports { if d.lang != lang { continue @@ -319,8 +342,8 @@ func (c *scalaConfig) getImplicitImports(lang, imp string) (deps []string) { return } -// configuredRules returns an ordered list of configured rules -func (c *scalaConfig) configuredRules() []*scalarule.Config { +// ConfiguredRules returns an ordered list of configured rules +func (c *Config) ConfiguredRules() []*scalarule.Config { names := make([]string, 0) for name := range c.rules { @@ -334,17 +357,17 @@ func (c *scalaConfig) configuredRules() []*scalarule.Config { return rules } -func (c *scalaConfig) shouldAnnotateWildcardImports() bool { +func (c *Config) ShouldAnnotateWildcardImports() bool { _, ok := c.annotations[DebugWildcardImports] return ok } -func (c *scalaConfig) shouldAnnotateImports() bool { +func (c *Config) ShouldAnnotateImports() bool { _, ok := c.annotations[DebugImports] return ok } -func (c *scalaConfig) shouldAnnotateExports() bool { +func (c *Config) ShouldAnnotateExports() bool { _, ok := c.annotations[DebugExports] return ok } @@ -353,7 +376,7 @@ func (c *scalaConfig) shouldAnnotateExports() bool { // should be resolved within the scope of the given filename pattern. // resolveFileSymbolNameSpecs represent a whitelist; if no patterns match, false // is returned. -func (c *scalaConfig) ShouldResolveFileSymbolName(filename, name string) bool { +func (c *Config) ShouldResolveFileSymbolName(filename, name string) bool { for _, spec := range c.resolveFileSymbolNames { if ok, _ := doublestar.Match(spec.pattern, filename); !ok { continue @@ -366,24 +389,24 @@ func (c *scalaConfig) ShouldResolveFileSymbolName(filename, name string) bool { return false } -func (c *scalaConfig) Comment() build.Comment { +func (c *Config) Comment() build.Comment { return build.Comment{Token: "# " + c.String()} } -func (c *scalaConfig) String() string { - return fmt.Sprintf("scalaConfig rel=%q, annotations=%+v", c.rel, c.annotations) +func (c *Config) String() string { + return fmt.Sprintf("Config rel=%q, annotations=%+v", c.rel, c.annotations) } -func (c *scalaConfig) maybeRewrite(kind string, from label.Label) label.Label { +func (c *Config) MaybeRewrite(kind string, from label.Label) label.Label { if spec, ok := c.labelNameRewrites[kind]; ok { return spec.Rewrite(from) } return from } -// mergeDeps takes the given list of existing deps and a list of dependency +// MergeDeps takes the given list of existing deps and a list of dependency // labels and merges it into a final list. -func mergeDeps(kind string, target *build.ListExpr, deps []label.Label) { +func MergeDeps(kind string, target *build.ListExpr, deps []label.Label) { for _, dep := range deps { str := &build.StringExpr{Value: dep.String()} target.List = append(target.List, str) @@ -401,7 +424,7 @@ func mergeDeps(kind string, target *build.ListExpr, deps []label.Label) { // cleanExports takes the given list of exports and removes those that are expected to // be provided again. -func (c *scalaConfig) cleanExports(from label.Label, current build.Expr, newExports []label.Label) *build.ListExpr { +func (c *Config) CleanExports(from label.Label, current build.Expr, newExports []label.Label) *build.ListExpr { incoming := make(map[label.Label]bool) for _, l := range newExports { incoming[l] = true @@ -427,7 +450,7 @@ func (c *scalaConfig) cleanExports(from label.Label, current build.Expr, newExpo // cleanDeps takes the given list of deps and removes those that are expected to // be provided again. -func (c *scalaConfig) cleanDeps(from label.Label, current build.Expr, newImports []label.Label) *build.ListExpr { +func (c *Config) CleanDeps(from label.Label, current build.Expr, newImports []label.Label) *build.ListExpr { incoming := make(map[label.Label]bool) for _, l := range newImports { incoming[l] = true @@ -451,7 +474,7 @@ func (c *scalaConfig) cleanDeps(from label.Label, current build.Expr, newImports return deps } -func (c *scalaConfig) shouldKeepExport(expr build.Expr) bool { +func (c *Config) shouldKeepExport(expr build.Expr) bool { // does it have a '# keep' directive? if rule.ShouldKeep(expr) { return true @@ -468,7 +491,7 @@ func (c *scalaConfig) shouldKeepExport(expr build.Expr) bool { return false } -func (c *scalaConfig) shouldKeepDep(expr build.Expr) bool { +func (c *Config) shouldKeepDep(expr build.Expr) bool { // does it have a '# keep' directive? if rule.ShouldKeep(expr) { return true @@ -483,7 +506,7 @@ func (c *scalaConfig) shouldKeepDep(expr build.Expr) bool { // if we can find a provider for this label, remove it (it should have been // resolved again if still wanted) - if c.canProvide(from) { + if c.CanProvide(from) { return false } diff --git a/language/scala/scala_config_test.go b/pkg/scalaconfig/config_test.go similarity index 86% rename from language/scala/scala_config_test.go rename to pkg/scalaconfig/config_test.go index a5decda6..33c1dead 100644 --- a/language/scala/scala_config_test.go +++ b/pkg/scalaconfig/config_test.go @@ -1,4 +1,4 @@ -package scala +package scalaconfig import ( "fmt" @@ -22,10 +22,10 @@ func TestScalaConfigParseDirectives(t *testing.T) { for name, tc := range map[string]struct { directives []rule.Directive wantErr error - want *scalaConfig + want *Config }{ "degenerate": { - want: &scalaConfig{ + want: &Config{ rules: map[string]*scalarule.Config{}, annotations: map[debugAnnotation]any{}, labelNameRewrites: map[string]resolver.LabelNameRewriteSpec{}, @@ -36,7 +36,7 @@ func TestScalaConfigParseDirectives(t *testing.T) { {Key: "scala_rule", Value: "scala_binary implementation @io_bazel_rules_scala//scala:scala.bzl%scala_binary"}, {Key: "scala_debug", Value: "imports"}, }, - want: &scalaConfig{ + want: &Config{ rules: map[string]*scalarule.Config{ "scala_binary": { Deps: map[string]bool{}, @@ -58,7 +58,7 @@ func TestScalaConfigParseDirectives(t *testing.T) { {Key: "scala_debug", Value: "imports"}, {Key: "scala_rule", Value: "scala_binary implementation @io_bazel_rules_scala//scala:scala.bzl%scala_binary"}, }, - want: &scalaConfig{ + want: &Config{ rules: map[string]*scalarule.Config{ "scala_binary": { Deps: map[string]bool{}, @@ -77,14 +77,14 @@ func TestScalaConfigParseDirectives(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - sc, err := newTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) + sc, err := NewTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) if testutil.ExpectError(t, tc.wantErr, err) { return } got := sc if diff := cmp.Diff(tc.want, got, - cmp.AllowUnexported(scalaConfig{}), - cmpopts.IgnoreFields(scalaConfig{}, "config", "universe"), + cmp.AllowUnexported(Config{}), + cmpopts.IgnoreFields(Config{}, "config", "universe"), cmpopts.IgnoreFields(scalarule.Config{}, "Config"), ); diff != "" { t.Errorf("(-want +got):\n%s", diff) @@ -137,7 +137,7 @@ func TestScalaConfigParseRuleDirective(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - sc, err := newTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) + sc, err := NewTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) if testutil.ExpectError(t, tc.wantErr, err) { return } @@ -182,7 +182,7 @@ func TestScalaConfigParseOverrideDirective(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - sc, err := newTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) + sc, err := NewTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) if testutil.ExpectError(t, tc.wantErr, err) { return } @@ -227,7 +227,7 @@ func TestScalaConfigParseImplicitImportDirective(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - sc, err := newTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) + sc, err := NewTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) if testutil.ExpectError(t, tc.wantErr, err) { return } @@ -276,7 +276,7 @@ func TestScalaConfigParseResolveFileSymbolName(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - sc, err := newTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) + sc, err := NewTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) if testutil.ExpectError(t, tc.wantErr, err) { return } @@ -326,7 +326,7 @@ func TestScalaConfigParseScalaAnnotate(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - sc, err := newTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) + sc, err := NewTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) if testutil.ExpectError(t, tc.wantErr, err) { return } @@ -357,7 +357,7 @@ func TestScalaConfigParseResolveKindRewriteNameDirective(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - sc, err := newTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) + sc, err := NewTestScalaConfig(t, mocks.NewUniverse(t), "", tc.directives...) if testutil.ExpectError(t, tc.wantErr, err) { return } @@ -422,7 +422,7 @@ func TestScalaConfigGetKnownRule(t *testing.T) { Times(tc.wantTimes). Return(nil, false) - sc := newScalaConfig(universe, c, tc.rel) + sc := New(universe, c, tc.rel) sc.GetKnownRule(tc.from) @@ -434,9 +434,67 @@ func TestScalaConfigGetKnownRule(t *testing.T) { } } -func newTestScalaConfig(t *testing.T, universe resolver.Universe, rel string, dd ...rule.Directive) (*scalaConfig, error) { - c := config.New() - sc := newScalaConfig(universe, c, rel) - err := sc.parseDirectives(dd) - return sc, err +func TestScalaDepLabel(t *testing.T) { + for name, tc := range map[string]struct { + in string + want label.Label + }{ + "degenerate": { + in: ` +test( + expr = "", +) + `, + want: label.NoLabel, + }, + "invalid label": { + in: ` +test( + expr = "@@@", +) + `, + want: label.NoLabel, + }, + "valid label": { + in: ` +test( + expr = "@foo//bar:baz", +) + `, + want: label.New("foo", "bar", "baz"), + }, + "invalid callexpr": { + in: ` +test( + expr = fn("@foo//bar:baz"), +) + `, + want: label.NoLabel, + }, + "valid callexpr": { + in: ` +test( + expr = scala_dep("@foo//bar:baz"), +) + `, + want: label.New("foo", "bar", "baz"), + }, + } { + t.Run(name, func(t *testing.T) { + file, err := rule.LoadData("", "BUILD", []byte(tc.in)) + if err != nil { + t.Fatal(err) + } + if len(file.Rules) != 1 { + t.Fatalf("expected single in rule, got %d", len(file.Rules)) + } + target := file.Rules[0] + expr := target.Attr("expr") + got := labelFromDepExpr(expr) + + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Errorf("label (-want +got):\n%s", diff) + } + }) + } } diff --git a/pkg/scalaconfig/test_config.go b/pkg/scalaconfig/test_config.go new file mode 100644 index 00000000..cd16bf66 --- /dev/null +++ b/pkg/scalaconfig/test_config.go @@ -0,0 +1,16 @@ +package scalaconfig + +import ( + "testing" + + "github.com/bazelbuild/bazel-gazelle/config" + "github.com/bazelbuild/bazel-gazelle/rule" + "github.com/stackb/scala-gazelle/pkg/resolver" +) + +func NewTestScalaConfig(t *testing.T, universe resolver.Universe, rel string, dd ...rule.Directive) (*Config, error) { + c := config.New() + sc := New(universe, c, rel) + err := sc.ParseDirectives(dd) + return sc, err +} From b776a1ff0a67763b051da9d77839c6cd88e02d93 Mon Sep 17 00:00:00 2001 From: Paul Johnston Date: Sun, 21 Jul 2024 17:41:24 -0600 Subject: [PATCH 5/5] Initial fix_wildcard_import experiment --- language/scala/BUILD.bazel | 2 + language/scala/existing_scala_rule.go | 6 + language/scala/fix_wildcard_imports.go | 259 ++++++++++++++++++ language/scala/fix_wildcard_imports_test.go | 61 +++++ language/scala/language.go | 2 +- language/scala/language_test.go | 8 +- language/scala/scala_package.go | 4 +- language/scala/scala_rule.go | 23 +- pkg/parser/BUILD.bazel | 1 + pkg/parser/mocks/Parser.go | 16 +- .../wildcard_import_expanding_parser.go | 33 ++- pkg/resolver/mocks/Universe.go | 37 +++ pkg/resolver/universe.go | 1 + pkg/scalaconfig/config.go | 2 +- pkg/scalaconfig/config_test.go | 2 +- 15 files changed, 419 insertions(+), 38 deletions(-) create mode 100644 language/scala/fix_wildcard_imports.go create mode 100644 language/scala/fix_wildcard_imports_test.go diff --git a/language/scala/BUILD.bazel b/language/scala/BUILD.bazel index 696f916e..ce3f7999 100644 --- a/language/scala/BUILD.bazel +++ b/language/scala/BUILD.bazel @@ -14,6 +14,7 @@ go_library( "cross_resolve.go", "existing_scala_rule.go", "fix.go", + "fix_wildcard_imports.go", "flags.go", "generate.go", "kinds.go", @@ -79,6 +80,7 @@ go_test( name = "scala_test", srcs = [ "existing_scala_rule_test.go", + "fix_wildcard_imports_test.go", "flags_test.go", "golden_test.go", "language_test.go", diff --git a/language/scala/existing_scala_rule.go b/language/scala/existing_scala_rule.go index 2ca6484a..626cf9aa 100644 --- a/language/scala/existing_scala_rule.go +++ b/language/scala/existing_scala_rule.go @@ -130,6 +130,12 @@ func (s *existingScalaRule) Resolve(rctx *scalarule.ResolveContext, importsRaw i r := rctx.Rule sc := scalaconfig.Get(rctx.Config) + if sc.ShouldAnnotateWildcardImports() { + if err := scalaRule.fixWildcardImports(); err != nil { + log.Fatalf("failed to fix wildcard imports: %v", err) + } + } + // part 1a: deps newImports := imports.Deps(sc.MaybeRewrite(r.Kind(), rctx.From)) diff --git a/language/scala/fix_wildcard_imports.go b/language/scala/fix_wildcard_imports.go new file mode 100644 index 00000000..ce62882e --- /dev/null +++ b/language/scala/fix_wildcard_imports.go @@ -0,0 +1,259 @@ +package scala + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/fs" + "log" + "os" + "os/exec" + "path" + "reflect" + "regexp" + "strings" + "syscall" + + sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" + "github.com/stackb/scala-gazelle/pkg/resolver" + "github.com/stackb/scala-gazelle/pkg/scalaconfig" +) + +func fixWildcardRuleImports(sc *scalaconfig.Config, rule *sppb.Rule) error { + if rule.Label != "//omnistac/gum/dao:auth_dao_scala" { + return nil + } + log.Println("fixing wildcards for rule:", rule.Label) + for _, file := range rule.Files { + if err := fixWildcardFileImports(sc, rule, file); err != nil { + return err + } + } + return nil +} + +func fixWildcardFileImports(sc *scalaconfig.Config, rule *sppb.Rule, file *sppb.File) error { + // log.Println("fixing wildcards for file:", file.Filename) + for _, imp := range file.Imports { + if err := fixWildcardSingleImport(sc, rule, file, imp); err != nil { + return err + } + } + return nil +} + +func fixWildcardSingleImport(sc *scalaconfig.Config, rule *sppb.Rule, file *sppb.File, imp string) error { + if _, ok := resolver.IsWildcardImport(imp); !ok { + return nil + } + if strings.HasSuffix(imp, ".api._") { + return nil + } + if strings.HasSuffix(imp, ".Implicits._") { + return nil + } + + filename := path.Join(sc.Config().WorkDir, sc.Rel(), file.Filename) + // log.Println("fixing wildcard for file:", filename, imp) + + targetLine := "import " + imp + sf, err := sliceFile(filename, targetLine) + if err != nil { + // return fmt.Errorf("failed to slice file: %v", err) + log.Printf("failed to slice file: %v", err) + return nil + } + + err = runFixLoop(rule, sf, strings.TrimSuffix(imp, "._")) + if err == nil { + return nil + } + log.Printf("fix error: %v (will restore original file)", err) + + // something went wrong - restore original file + if restoreErr := sf.restore(); restoreErr != nil { + return restoreErr + } + + return err +} + +func runFixLoop(rule *sppb.Rule, sf *slicedFile, importPrefix string) error { + + // on each build, parse the output for notFound symbols. Stop the loop when + // the output is the same as the previous one (nothing more actionable). + previouslyNotFound := []string{} + + var iteration int + for { + iteration++ + + output, exitCode, err := bazelBuild("bazel", rule.Label) + if err != nil { + return err + } + + log.Printf(">>> [%s][%d]: fixing %s._\n", sf.filename, iteration, importPrefix) + log.Println(string(output)) + + // on the first iteration ensure the target builds so we have a clean + // slate. Then, comment out the targetLine (and rebuild...) + if iteration == 1 { + if exitCode != 0 { + return fmt.Errorf("%v: target must build first time: %v", rule.Label, err) + } + if err := sf.write(fmt.Sprintf("// import %s._", importPrefix)); err != nil { + return err + } + continue + } + + // on subsequent iterations if the exitCode is 0, the process is successful. + if exitCode == 0 { + return nil + } + + // scan the output for symbols that were not found + notFound, err := scanOutputForNotFound(output) + if err != nil { + return err + } + + // if no notFound symbols were found, the process failed, but we have + // nothing actionable. + if reflect.DeepEqual(previouslyNotFound, notFound) { + return fmt.Errorf("expand wildcard failed: final set of notFound symbols: %v", notFound) + } + + // rewrite the file with the updated import (and continue) + if err := sf.write(makeImportLine(importPrefix, notFound)); err != nil { + return fmt.Errorf("failed to write split file: %v", err) + } + } +} + +func makeImportLine(importPrefix string, symbols []string) string { + return fmt.Sprintf("import %s.{%s}", importPrefix, strings.Join(symbols, ", ")) +} + +type slicedFile struct { + filename string + info fs.FileInfo + + beforeLines []string + targetLine string + afterLines []string +} + +func (f *slicedFile) restore() error { + return f.write(f.targetLine) +} + +func (f *slicedFile) write(targetLine string) error { + lines := append(f.beforeLines, targetLine) + lines = append(lines, f.afterLines...) + content := strings.Join(lines, "\n") + data := []byte(content) + if err := os.WriteFile(f.filename, data, f.info.Mode()); err != nil { + return err + } + return nil +} + +func sliceFile(filename string, targetLine string) (*slicedFile, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + info, err := f.Stat() + if err != nil { + return nil, err + } + return sliceInput(filename, info, f, targetLine) +} + +func sliceInput(filename string, info fs.FileInfo, in io.Reader, targetLine string) (*slicedFile, error) { + file := new(slicedFile) + file.filename = filename + file.info = info + + scanner := bufio.NewScanner(in) + for scanner.Scan() { + line := scanner.Text() + if line == targetLine { + file.targetLine = line + continue + } + if file.targetLine == "" { + file.beforeLines = append(file.beforeLines, line) + } else { + file.afterLines = append(file.afterLines, line) + } + } + if file.targetLine == "" { + return nil, fmt.Errorf("%s: slice target line not found: %v", filename, targetLine) + } + + // add a final entry to afterLines so that the file ends with a single newline + file.afterLines = append(file.afterLines, "") + + return file, nil +} + +// omnistac/gum/testutils/DbDataInitUtils.scala:98: error: [rewritten by -quickfix] not found: value FixSessionDao +var notFoundLine = regexp.MustCompile(`^(.*):\d+: error: .*not found: (value|type) (.*)$`) + +func bazelBuild(bazelExe string, label string) ([]byte, int, error) { + args := []string{"build", label} + + command := exec.Command(bazelExe, args...) + command.Dir = getCommandDir() + + log.Println("!!!", command.String()) + output, err := command.CombinedOutput() + log.Println("cmdErr:", err) + if err != nil { + // Check for exit errors specifically + if exitError, ok := err.(*exec.ExitError); ok { + waitStatus := exitError.Sys().(syscall.WaitStatus) + exitCode := waitStatus.ExitStatus() + return nil, exitCode, err + } else { + return nil, -1, err + } + } + return output, 0, nil +} + +func scanOutputForNotFound(output []byte) ([]string, error) { + notFound := make([]string, 0) + + scanner := bufio.NewScanner(bytes.NewReader(output)) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" { + continue + } + log.Println("line:", line) + if match := notFoundLine.FindStringSubmatch(line); match != nil { + typeOrValue := match[3] + notFound = append(notFound, typeOrValue) + continue + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + + return notFound, nil +} + +func getCommandDir() string { + if bwd, ok := os.LookupEnv("BUILD_WORKSPACE_DIRECTORY"); ok { + return bwd + } else { + return "." + } +} diff --git a/language/scala/fix_wildcard_imports_test.go b/language/scala/fix_wildcard_imports_test.go new file mode 100644 index 00000000..b25f1c6b --- /dev/null +++ b/language/scala/fix_wildcard_imports_test.go @@ -0,0 +1,61 @@ +package scala + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestScanOutputForNotFound(t *testing.T) { + for name, tc := range map[string]struct { + output string + wantErr string + want []string + }{ + "degenerate": { + want: []string{}, + }, + "example 1": { + output: ` +ERROR: /Users/pcj/go/src/github.com/Omnistac/unity/omnistac/gum/dao/BUILD.bazel:48:21: scala @//omnistac/gum/dao:auth_dao_scala failed: (Exit 1): scalac failed: error executing command (from target //omnistac/gum/dao:auth_dao_scala) bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac '--jvm_flag=-Xss32M' ... (remaining 1 argument skipped) +omnistac/gum/dao/AuthDao.scala:15: error: [rewritten by -quickfix] not found: type ZonedDateTime + passwordLastUpdatedTimestamp: ZonedDateTime = ZonedDateTime.now(DateUtils.SYSTEM_TZ), + ^ +omnistac/gum/dao/AuthDao.scala:15: error: [rewritten by -quickfix] not found: value ZonedDateTime + passwordLastUpdatedTimestamp: ZonedDateTime = ZonedDateTime.now(DateUtils.SYSTEM_TZ), + ^ +omnistac/gum/dao/AuthDao.scala:32: error: [rewritten by -quickfix] not found: type ZonedDateTime + def putNewPasswordToken(userId: ActorId, token: String, expirationTs: ZonedDateTime): Future[ResponseStatus] + ^ +3 errors +Build failed +java.lang.RuntimeException: Build failed + at io.bazel.rulesscala.scalac.ScalacWorker.compileScalaSources(ScalacWorker.java:324) + at io.bazel.rulesscala.scalac.ScalacWorker.work(ScalacWorker.java:72) + at io.bazel.rulesscala.worker.Worker.persistentWorkerMain(Worker.java:86) + at io.bazel.rulesscala.worker.Worker.workerMain(Worker.java:39) + at io.bazel.rulesscala.scalac.ScalacWorker.main(ScalacWorker.java:36) +Target //omnistac/gum/dao:auth_dao_scala failed to build +Use --verbose_failures to see the command lines of failed build steps. +INFO: Elapsed time: 2.354s, Critical Path: 1.40s +INFO: 2 processes: 2 internal. +FAILED: Build did NOT complete successfully +`, + want: []string{"ZonedDateTime"}, + }, + } { + t.Run(name, func(t *testing.T) { + got, err := scanOutputForNotFound([]byte(tc.output)) + var gotErr string + if err != nil { + gotErr = err.Error() + } + if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" { + t.Errorf("error (-want +got):\n%s", diff) + } + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Errorf("result (-want +got):\n%s", diff) + } + }) + } +} diff --git a/language/scala/language.go b/language/scala/language.go index 6ca2875c..ec139607 100644 --- a/language/scala/language.go +++ b/language/scala/language.go @@ -121,7 +121,7 @@ func NewLanguage() language.Language { writeParseProgress(lang.progress, msg) } }) - lang.parser = parser.NewMemoParser(parser.NewWildcardExpandingParser(lang.sourceProvider)) + lang.parser = parser.NewMemoParser(lang.sourceProvider) lang.AddSymbolProvider(lang.sourceProvider) lang.AddSymbolProvider(provider.NewJavaProvider()) diff --git a/language/scala/language_test.go b/language/scala/language_test.go index 817449eb..06e2beb4 100644 --- a/language/scala/language_test.go +++ b/language/scala/language_test.go @@ -15,11 +15,11 @@ func ExampleLanguage_KnownDirectives() { fmt.Println(d) } // output: + // scala_debug + // scala_rule + // resolve_glob // resolve_conflicts + // resolve_with // resolve_file_symbol_name - // resolve_glob // resolve_kind_rewrite_name - // resolve_with - // scala_debug - // scala_rule } diff --git a/language/scala/scala_package.go b/language/scala/scala_package.go index 033c5f3b..cfe80dcf 100644 --- a/language/scala/scala_package.go +++ b/language/scala/scala_package.go @@ -217,7 +217,9 @@ func (s *scalaPackage) ParseRule(r *rule.Rule, attrName string) (scalarule.Rule, scope: s.universe, } - return newScalaRule(ctx, rule), nil + sr := newScalaRule(ctx, rule) + + return sr, nil } // repoRootDir return the root directory of the repo. diff --git a/language/scala/scala_rule.go b/language/scala/scala_rule.go index 12bd4bbd..17266499 100644 --- a/language/scala/scala_rule.go +++ b/language/scala/scala_rule.go @@ -141,12 +141,6 @@ func (r *scalaRule) ResolveImports(rctx *scalarule.ResolveContext) resolver.Impo continue // skip this item if conflict strategy says "ok" but returns nil (it's a wildcard import) } } else { - if r.ctx.scalaConfig.ShouldAnnotateWildcardImports() && item.sym.Type == sppb.ImportType_PROTO_PACKAGE { - if scope, ok := r.ctx.scope.GetScope(item.imp.Imp); ok { - wildcardImport := item.imp.Src // original symbol name having underscore suffix - r.handleWildcardImport(item.imp.Source, wildcardImport, scope) - } - } fmt.Println(resolver.SymbolConfictMessage(item.sym, item.imp, rctx.From)) } } @@ -387,19 +381,6 @@ func (r *scalaRule) fileImports(imports resolver.ImportMap, file *sppb.File) { } } -func (r *scalaRule) handleWildcardImport(file *sppb.File, imp string, scope resolver.Scope) { - names := make([]string, 0) - for _, name := range file.Names { - if _, ok := scope.GetSymbol(name); ok { - names = append(names, name) - } - } - if len(names) > 0 { - sort.Strings(names) - log.Printf("[%s]: import %s.{%s}", file.Filename, strings.TrimSuffix(imp, "._"), strings.Join(names, ", ")) - } -} - // Provides implements part of the scalarule.Rule interface. func (r *scalaRule) Provides() []resolve.ImportSpec { exports := make([]resolve.ImportSpec, 0, len(r.exports)) @@ -438,6 +419,10 @@ func (r *scalaRule) putExport(imp string) { r.exports[imp] = resolve.ImportSpec{Imp: imp, Lang: scalaLangName} } +func (r *scalaRule) fixWildcardImports() error { + return fixWildcardRuleImports(r.ctx.scalaConfig, r.pb) +} + func isBinaryRule(kind string) bool { return strings.Contains(kind, "binary") || strings.Contains(kind, "test") } diff --git a/pkg/parser/BUILD.bazel b/pkg/parser/BUILD.bazel index e9ef4ec5..54ea2b57 100644 --- a/pkg/parser/BUILD.bazel +++ b/pkg/parser/BUILD.bazel @@ -21,6 +21,7 @@ go_library( "//build/stack/gazelle/scala/parse", "//pkg/collections", "//pkg/resolver", + "//pkg/scalaconfig", "@bazel_gazelle//config:go_default_library", "@bazel_gazelle//label:go_default_library", "@com_github_amenzhinsky_go_memexec//:go-memexec", diff --git a/pkg/parser/mocks/Parser.go b/pkg/parser/mocks/Parser.go index e85eac36..822f29db 100644 --- a/pkg/parser/mocks/Parser.go +++ b/pkg/parser/mocks/Parser.go @@ -3,7 +3,9 @@ package mocks import ( + config "github.com/bazelbuild/bazel-gazelle/config" label "github.com/bazelbuild/bazel-gazelle/label" + mock "github.com/stretchr/testify/mock" parse "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" @@ -28,20 +30,20 @@ func (_m *Parser) LoadScalaRule(from label.Label, rule *parse.Rule) error { return r0 } -// ParseScalaRule provides a mock function with given fields: kind, from, dir, srcs -func (_m *Parser) ParseScalaRule(kind string, from label.Label, dir string, srcs ...string) (*parse.Rule, error) { +// ParseScalaRule provides a mock function with given fields: cfg, kind, from, dir, srcs +func (_m *Parser) ParseScalaRule(cfg *config.Config, kind string, from label.Label, dir string, srcs ...string) (*parse.Rule, error) { _va := make([]interface{}, len(srcs)) for _i := range srcs { _va[_i] = srcs[_i] } var _ca []interface{} - _ca = append(_ca, kind, from, dir) + _ca = append(_ca, cfg, kind, from, dir) _ca = append(_ca, _va...) ret := _m.Called(_ca...) var r0 *parse.Rule - if rf, ok := ret.Get(0).(func(string, label.Label, string, ...string) *parse.Rule); ok { - r0 = rf(kind, from, dir, srcs...) + if rf, ok := ret.Get(0).(func(*config.Config, string, label.Label, string, ...string) *parse.Rule); ok { + r0 = rf(cfg, kind, from, dir, srcs...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*parse.Rule) @@ -49,8 +51,8 @@ func (_m *Parser) ParseScalaRule(kind string, from label.Label, dir string, srcs } var r1 error - if rf, ok := ret.Get(1).(func(string, label.Label, string, ...string) error); ok { - r1 = rf(kind, from, dir, srcs...) + if rf, ok := ret.Get(1).(func(*config.Config, string, label.Label, string, ...string) error); ok { + r1 = rf(cfg, kind, from, dir, srcs...) } else { r1 = ret.Error(1) } diff --git a/pkg/parser/wildcard_import_expanding_parser.go b/pkg/parser/wildcard_import_expanding_parser.go index 085adfc2..21ba40ec 100644 --- a/pkg/parser/wildcard_import_expanding_parser.go +++ b/pkg/parser/wildcard_import_expanding_parser.go @@ -7,6 +7,7 @@ import ( "github.com/bazelbuild/bazel-gazelle/label" sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse" "github.com/stackb/scala-gazelle/pkg/resolver" + "github.com/stackb/scala-gazelle/pkg/scalaconfig" ) const debugWildcardImportExpandingParser = false @@ -31,10 +32,14 @@ func (p *WildcardImportExpandingParser) ParseScalaRule(c *config.Config, kind st return nil, err } - for _, file := range rule.Files { - for _, imp := range file.Imports { - if _, ok := resolver.IsWildcardImport(imp); ok { - log.Printf("%s: wildcard import: %s", file.Filename, imp) + sc := scalaconfig.Get(c) + + if sc.ShouldAnnotateWildcardImports() { + for _, file := range rule.Files { + for _, imp := range file.Imports { + if _, ok := resolver.IsWildcardImport(imp); ok { + log.Printf("❗❗❗ %s: wildcard import: %s", file.Filename, imp) + } } } } @@ -45,3 +50,23 @@ func (p *WildcardImportExpandingParser) ParseScalaRule(c *config.Config, kind st func (p *WildcardImportExpandingParser) LoadScalaRule(from label.Label, rule *sppb.Rule) error { return p.next.LoadScalaRule(from, rule) } + +// if r.ctx.scalaConfig.ShouldAnnotateWildcardImports() && item.sym.Type == sppb.ImportType_PROTO_PACKAGE { +// if scope, ok := r.ctx.scope.GetScope(item.imp.Imp); ok { +// wildcardImport := item.imp.Src // original symbol name having underscore suffix +// r.handleWildcardImport(item.imp.Source, wildcardImport, scope) +// } +// } + +// func (r *scalaRule) handleWildcardImport(file *sppb.File, imp string, scope resolver.Scope) { +// names := make([]string, 0) +// for _, name := range file.Names { +// if _, ok := scope.GetSymbol(name); ok { +// names = append(names, name) +// } +// } +// if len(names) > 0 { +// sort.Strings(names) +// log.Printf("[%s]: import %s.{%s}", file.Filename, strings.TrimSuffix(imp, "._"), strings.Join(names, ", ")) +// } +// } diff --git a/pkg/resolver/mocks/Universe.go b/pkg/resolver/mocks/Universe.go index d37408c1..c54b516f 100644 --- a/pkg/resolver/mocks/Universe.go +++ b/pkg/resolver/mocks/Universe.go @@ -57,6 +57,29 @@ func (_m *Universe) GetConflictResolver(name string) (resolver.ConflictResolver, return r0, r1 } +// GetKnownFile provides a mock function with given fields: pkg +func (_m *Universe) GetKnownFile(pkg string) (*rule.File, bool) { + ret := _m.Called(pkg) + + var r0 *rule.File + if rf, ok := ret.Get(0).(func(string) *rule.File); ok { + r0 = rf(pkg) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*rule.File) + } + } + + var r1 bool + if rf, ok := ret.Get(1).(func(string) bool); ok { + r1 = rf(pkg) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + // GetKnownRule provides a mock function with given fields: from func (_m *Universe) GetKnownRule(from label.Label) (*rule.Rule, bool) { ret := _m.Called(from) @@ -156,6 +179,20 @@ func (_m *Universe) PutConflictResolver(name string, r resolver.ConflictResolver return r0 } +// PutKnownFile provides a mock function with given fields: pkg, r +func (_m *Universe) PutKnownFile(pkg string, r *rule.File) error { + ret := _m.Called(pkg, r) + + var r0 error + if rf, ok := ret.Get(0).(func(string, *rule.File) error); ok { + r0 = rf(pkg, r) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // PutKnownRule provides a mock function with given fields: from, r func (_m *Universe) PutKnownRule(from label.Label, r *rule.Rule) error { ret := _m.Called(from, r) diff --git a/pkg/resolver/universe.go b/pkg/resolver/universe.go index 6ec45223..0cc457ad 100644 --- a/pkg/resolver/universe.go +++ b/pkg/resolver/universe.go @@ -5,6 +5,7 @@ package resolver type Universe interface { SymbolProviderRegistry KnownRuleRegistry + KnownFileRegistry ConflictResolverRegistry Scope SymbolResolver diff --git a/pkg/scalaconfig/config.go b/pkg/scalaconfig/config.go index 6ceab0c3..161597cf 100644 --- a/pkg/scalaconfig/config.go +++ b/pkg/scalaconfig/config.go @@ -567,7 +567,7 @@ type resolveFileSymbolNameSpec struct { func parseAnnotation(val string) debugAnnotation { switch val { - case "wildcard-imports": + case "wildcardimports": return DebugWildcardImports case "imports": return DebugImports diff --git a/pkg/scalaconfig/config_test.go b/pkg/scalaconfig/config_test.go index 33c1dead..41848a79 100644 --- a/pkg/scalaconfig/config_test.go +++ b/pkg/scalaconfig/config_test.go @@ -318,7 +318,7 @@ func TestScalaConfigParseScalaAnnotate(t *testing.T) { }, "wildcards": { directives: []rule.Directive{ - {Key: scalaDebugDirective, Value: "wildcard-imports"}, + {Key: scalaDebugDirective, Value: "wildcardimports"}, }, want: map[debugAnnotation]interface{}{ DebugWildcardImports: nil,