diff --git a/java/gazelle/configure.go b/java/gazelle/configure.go index 19006343..e2b405b3 100644 --- a/java/gazelle/configure.go +++ b/java/gazelle/configure.go @@ -8,6 +8,7 @@ import ( "github.com/bazel-contrib/rules_jvm/java/gazelle/javaconfig" "github.com/bazel-contrib/rules_jvm/java/gazelle/private/javaparser" "github.com/bazel-contrib/rules_jvm/java/gazelle/private/maven" + "github.com/bazel-contrib/rules_jvm/java/gazelle/private/types" "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/rule" bzl "github.com/bazelbuild/buildtools/build" @@ -64,6 +65,7 @@ func (jc *Configurer) KnownDirectives() []string { javaconfig.JavaTestMode, javaconfig.JavaGenerateProto, javaconfig.JavaMavenRepositoryName, + javaconfig.JavaAnnotationProcessorPlugin, } } @@ -129,6 +131,21 @@ func (jc *Configurer) Configure(c *config.Config, rel string, f *rule.File) { jc.lang.logger.Fatal().Msgf("invalid value for directive %q: %s: possible values are true/false", javaconfig.JavaGenerateProto, d.Value) } + case javaconfig.JavaAnnotationProcessorPlugin: + // Format: # gazelle:java_annotation_processor_plugin com.example.AnnotationName com.example.AnnotationProcessorImpl + parts := strings.Split(d.Value, " ") + if len(parts) != 2 { + jc.lang.logger.Fatal().Msgf("invalid value for directive %q: %s: expected an annotation class-name followed by a processor class-name", javaconfig.JavaAnnotationProcessorPlugin, d.Value) + } + annotationClassName, err := types.ParseClassName(parts[0]) + if err != nil { + jc.lang.logger.Fatal().Msgf("invalid value for directive %q: %q: couldn't parse annotation processor annotation class-name: %v", javaconfig.JavaAnnotationProcessorPlugin, parts[0], err) + } + processorClassName, err := types.ParseClassName(parts[1]) + if err != nil { + jc.lang.logger.Fatal().Msgf("invalid value for directive %q: %q: couldn't parse annotation processor class-name: %v", javaconfig.JavaAnnotationProcessorPlugin, parts[1], err) + } + cfg.AddAnnotationProcessorPlugin(*annotationClassName, *processorClassName) } } } diff --git a/java/gazelle/generate.go b/java/gazelle/generate.go index 1011ef86..babd90ee 100644 --- a/java/gazelle/generate.go +++ b/java/gazelle/generate.go @@ -127,6 +127,8 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes // All java packages present in this bazel package. allPackageNames := sorted_set.NewSortedSetFn([]types.PackageName{}, types.PackageNameLess) + annotationProcessorClasses := sorted_set.NewSortedSetFn(nil, types.ClassNameLess) + if isModule { for mRel, mJavaPkg := range l.javaPackageCache { if !strings.HasPrefix(mRel, args.Rel) { @@ -152,6 +154,9 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes accumulateJavaFile(cfg, testJavaFiles, testHelperJavaFiles, separateTestJavaFiles, file, mJavaPkg.PerClassMetadata, log) } } + for _, annotationClass := range mJavaPkg.AllAnnotations().SortedSlice() { + annotationProcessorClasses.AddAll(cfg.GetAnnotationProcessorPluginClasses(annotationClass)) + } } } else { allPackageNames.Add(javaPkg.Name) @@ -174,6 +179,9 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes productionJavaFiles.Add(path) } } + for _, annotationClass := range javaPkg.AllAnnotations().SortedSlice() { + annotationProcessorClasses.AddAll(cfg.GetAnnotationProcessorPluginClasses(annotationClass)) + } } allPackageNamesSlice := allPackageNames.SortedSlice() @@ -192,7 +200,7 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes } if productionJavaFiles.Len() > 0 { - l.generateJavaLibrary(args.File, args.Rel, filepath.Base(args.Rel), productionJavaFiles.SortedSlice(), allPackageNames, nonLocalProductionJavaImports, nonLocalJavaExports, false, javaLibraryKind, &res) + l.generateJavaLibrary(args.File, args.Rel, filepath.Base(args.Rel), productionJavaFiles.SortedSlice(), allPackageNames, nonLocalProductionJavaImports, nonLocalJavaExports, annotationProcessorClasses, false, javaLibraryKind, &res) } var testHelperJavaClasses *sorted_set.SortedSet[types.ClassName] @@ -228,7 +236,7 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes testJavaImportsWithHelpers.Add(tf.pkg) srcs = append(srcs, tf.pathRelativeToBazelWorkspaceRoot) } - l.generateJavaLibrary(args.File, args.Rel, filepath.Base(args.Rel), srcs, packages, testJavaImports, nonLocalJavaExports, true, javaLibraryKind, &res) + l.generateJavaLibrary(args.File, args.Rel, filepath.Base(args.Rel), srcs, packages, testJavaImports, nonLocalJavaExports, annotationProcessorClasses, true, javaLibraryKind, &res) } } @@ -240,7 +248,7 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes case "file": for _, tf := range testJavaFiles.SortedSlice() { separateJavaTestReasons := separateTestJavaFiles[tf] - l.generateJavaTest(args.File, args.Rel, cfg.MavenRepositoryName(), tf, isModule, testJavaImportsWithHelpers, nil, separateJavaTestReasons.wrapper, separateJavaTestReasons.attributes, &res) + l.generateJavaTest(args.File, args.Rel, cfg.MavenRepositoryName(), tf, isModule, testJavaImportsWithHelpers, annotationProcessorClasses, nil, separateJavaTestReasons.wrapper, separateJavaTestReasons.attributes, &res) } case "suite": @@ -268,6 +276,7 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes packageNames, cfg.MavenRepositoryName(), testJavaImportsWithHelpers, + annotationProcessorClasses, cfg.GetCustomJavaTestFileSuffixes(), testHelperJavaFiles.Len() > 0, &res, @@ -284,7 +293,7 @@ func (l javaLang) GenerateRules(args language.GenerateArgs) language.GenerateRes testHelperDep = ptr(testHelperLibname(suiteName)) } separateJavaTestReasons := separateTestJavaFiles[src] - l.generateJavaTest(args.File, args.Rel, cfg.MavenRepositoryName(), src, isModule, testJavaImportsWithHelpers, testHelperDep, separateJavaTestReasons.wrapper, separateJavaTestReasons.attributes, &res) + l.generateJavaTest(args.File, args.Rel, cfg.MavenRepositoryName(), src, isModule, testJavaImportsWithHelpers, annotationProcessorClasses, testHelperDep, separateJavaTestReasons.wrapper, separateJavaTestReasons.attributes, &res) } } } @@ -453,7 +462,7 @@ func accumulateJavaFile(cfg *javaconfig.Config, testJavaFiles, testHelperJavaFil } } -func (l javaLang) generateJavaLibrary(file *rule.File, pathToPackageRelativeToBazelWorkspace string, name string, srcsRelativeToBazelWorkspace []string, packages, imports *sorted_set.SortedSet[types.PackageName], exports *sorted_set.SortedSet[types.PackageName], testonly bool, javaLibraryRuleKind string, res *language.GenerateResult) { +func (l javaLang) generateJavaLibrary(file *rule.File, pathToPackageRelativeToBazelWorkspace string, name string, srcsRelativeToBazelWorkspace []string, packages, imports *sorted_set.SortedSet[types.PackageName], exports *sorted_set.SortedSet[types.PackageName], annotationProcessorClasses *sorted_set.SortedSet[types.ClassName], testonly bool, javaLibraryRuleKind string, res *language.GenerateResult) { const ruleKind = "java_library" r := rule.NewRule(ruleKind, name) @@ -487,6 +496,7 @@ func (l javaLang) generateJavaLibrary(file *rule.File, pathToPackageRelativeToBa PackageNames: packages, ImportedPackageNames: imports, ExportedPackageNames: exports, + AnnotationProcessors: annotationProcessorClasses, } res.Imports = append(res.Imports, resolveInput) } @@ -511,7 +521,7 @@ func (l javaLang) generateJavaBinary(file *rule.File, m types.ClassName, libName }) } -func (l javaLang) generateJavaTest(file *rule.File, pathToPackageRelativeToBazelWorkspace string, mavenRepositoryName string, f javaFile, includePackageInName bool, imports *sorted_set.SortedSet[types.PackageName], depOnTestHelpers *string, wrapper string, extraAttributes map[string]bzl.Expr, res *language.GenerateResult) { +func (l javaLang) generateJavaTest(file *rule.File, pathToPackageRelativeToBazelWorkspace string, mavenRepositoryName string, f javaFile, includePackageInName bool, imports *sorted_set.SortedSet[types.PackageName], annotationProcessorClasses *sorted_set.SortedSet[types.ClassName], depOnTestHelpers *string, wrapper string, extraAttributes map[string]bzl.Expr, res *language.GenerateResult) { className := f.ClassName() fullyQualifiedTestClass := className.FullyQualifiedClassName() var testName string @@ -571,6 +581,7 @@ func (l javaLang) generateJavaTest(file *rule.File, pathToPackageRelativeToBazel resolveInput := types.ResolveInput{ PackageNames: sorted_set.NewSortedSetFn([]types.PackageName{f.pkg}, types.PackageNameLess), ImportedPackageNames: testImports, + AnnotationProcessors: annotationProcessorClasses, } res.Imports = append(res.Imports, resolveInput) } @@ -598,7 +609,7 @@ var junit5RuntimeDeps = []string{ "org.junit.platform:junit-platform-reporting", } -func (l javaLang) generateJavaTestSuite(file *rule.File, name string, srcs []string, packageNames *sorted_set.SortedSet[types.PackageName], mavenRepositoryName string, imports *sorted_set.SortedSet[types.PackageName], customTestSuffixes *[]string, hasHelpers bool, res *language.GenerateResult) { +func (l javaLang) generateJavaTestSuite(file *rule.File, name string, srcs []string, packageNames *sorted_set.SortedSet[types.PackageName], mavenRepositoryName string, imports *sorted_set.SortedSet[types.PackageName], annotationProcessorClasses *sorted_set.SortedSet[types.ClassName], customTestSuffixes *[]string, hasHelpers bool, res *language.GenerateResult) { const ruleKind = "java_test_suite" r := rule.NewRule(ruleKind, name) r.SetAttr("srcs", srcs) @@ -636,6 +647,7 @@ func (l javaLang) generateJavaTestSuite(file *rule.File, name string, srcs []str resolveInput := types.ResolveInput{ PackageNames: packageNames, ImportedPackageNames: suiteImports, + AnnotationProcessors: annotationProcessorClasses, } res.Imports = append(res.Imports, resolveInput) } diff --git a/java/gazelle/generate_test.go b/java/gazelle/generate_test.go index 1dcc28a0..418f280c 100644 --- a/java/gazelle/generate_test.go +++ b/java/gazelle/generate_test.go @@ -159,7 +159,7 @@ func TestSingleJavaTestFile(t *testing.T) { var res language.GenerateResult l := newTestJavaLang(t) - l.generateJavaTest(nil, "", "maven", f, tc.includePackageInName, stringsToPackageNames(tc.importedPackages), nil, tc.wrapper, nil, &res) + l.generateJavaTest(nil, "", "maven", f, tc.includePackageInName, stringsToPackageNames(tc.importedPackages), nil, nil, tc.wrapper, nil, &res) require.Len(t, res.Gen, 1, "want 1 generated rule") @@ -252,7 +252,7 @@ func TestSuite(t *testing.T) { var res language.GenerateResult l := newTestJavaLang(t) - l.generateJavaTestSuite(nil, "blah", []string{src}, stringsToPackageNames([]string{pkg}), "maven", stringsToPackageNames(tc.importedPackages), nil, false, &res) + l.generateJavaTestSuite(nil, "blah", []string{src}, stringsToPackageNames([]string{pkg}), "maven", stringsToPackageNames(tc.importedPackages), nil, nil, false, &res) require.Len(t, res.Gen, 1, "want 1 generated rule") diff --git a/java/gazelle/javaconfig/BUILD.bazel b/java/gazelle/javaconfig/BUILD.bazel index d8b7accc..bb2b756f 100644 --- a/java/gazelle/javaconfig/BUILD.bazel +++ b/java/gazelle/javaconfig/BUILD.bazel @@ -9,6 +9,8 @@ go_library( importpath = "github.com/bazel-contrib/rules_jvm/java/gazelle/javaconfig", visibility = ["//visibility:public"], deps = [ + "//java/gazelle/private/sorted_set", + "//java/gazelle/private/types", "@com_github_bazelbuild_buildtools//build", ], ) diff --git a/java/gazelle/javaconfig/config.go b/java/gazelle/javaconfig/config.go index 66a9a852..a3cc492c 100644 --- a/java/gazelle/javaconfig/config.go +++ b/java/gazelle/javaconfig/config.go @@ -6,6 +6,8 @@ import ( "path/filepath" "strings" + "github.com/bazel-contrib/rules_jvm/java/gazelle/private/sorted_set" + "github.com/bazel-contrib/rules_jvm/java/gazelle/private/types" bzl "github.com/bazelbuild/buildtools/build" ) @@ -47,6 +49,10 @@ const ( // JavaMavenRepositoryName tells the code generator what the repository name that contains all maven dependencies is. // Defaults to "maven" JavaMavenRepositoryName = "java_maven_repository_name" + + // JavaAnnotationProcessorPlugin tells the code generator about specific java_plugin targets needed to process + // specific annotations. + JavaAnnotationProcessorPlugin = "java_annotation_processor_plugin" ) // Configs is an extension of map[string]*Config. It provides finding methods @@ -60,6 +66,10 @@ func (c *Config) NewChild() *Config { for key, value := range c.excludedArtifacts { clonedExcludedArtifacts[key] = value } + annotationProcessorFullQualifiedClassToPluginClass := make(map[string]*sorted_set.SortedSet[types.ClassName]) + for key, value := range c.annotationProcessorFullQualifiedClassToPluginClass { + annotationProcessorFullQualifiedClassToPluginClass[key] = value.Clone() + } return &Config{ parent: c, extensionEnabled: c.extensionEnabled, @@ -74,6 +84,7 @@ func (c *Config) NewChild() *Config { annotationToWrapper: c.annotationToWrapper, excludedArtifacts: clonedExcludedArtifacts, mavenRepositoryName: c.mavenRepositoryName, + annotationProcessorFullQualifiedClassToPluginClass: annotationProcessorFullQualifiedClassToPluginClass, } } @@ -91,18 +102,19 @@ func (c *Configs) ParentForPackage(pkg string) *Config { type Config struct { parent *Config - extensionEnabled bool - isModuleRoot bool - generateProto bool - mavenInstallFile string - moduleGranularity string - repoRoot string - testMode string - customTestFileSuffixes *[]string - excludedArtifacts map[string]struct{} - annotationToAttribute map[string]map[string]bzl.Expr - annotationToWrapper map[string]string - mavenRepositoryName string + extensionEnabled bool + isModuleRoot bool + generateProto bool + mavenInstallFile string + moduleGranularity string + repoRoot string + testMode string + customTestFileSuffixes *[]string + excludedArtifacts map[string]struct{} + annotationToAttribute map[string]map[string]bzl.Expr + annotationToWrapper map[string]string + mavenRepositoryName string + annotationProcessorFullQualifiedClassToPluginClass map[string]*sorted_set.SortedSet[types.ClassName] } type LoadInfo struct { @@ -125,6 +137,7 @@ func New(repoRoot string) *Config { annotationToAttribute: make(map[string]map[string]bzl.Expr), annotationToWrapper: make(map[string]string), mavenRepositoryName: "maven", + annotationProcessorFullQualifiedClassToPluginClass: make(map[string]*sorted_set.SortedSet[types.ClassName]), } } @@ -269,6 +282,18 @@ func (c *Config) IsTestRule(ruleKind string) bool { return false } +func (c *Config) GetAnnotationProcessorPluginClasses(annotationClass types.ClassName) *sorted_set.SortedSet[types.ClassName] { + return c.annotationProcessorFullQualifiedClassToPluginClass[annotationClass.FullyQualifiedClassName()] +} + +func (c *Config) AddAnnotationProcessorPlugin(annotationClass types.ClassName, processorClass types.ClassName) { + fullyQualifiedAnnotationClass := annotationClass.FullyQualifiedClassName() + if _, ok := c.annotationProcessorFullQualifiedClassToPluginClass[fullyQualifiedAnnotationClass]; !ok { + c.annotationProcessorFullQualifiedClassToPluginClass[fullyQualifiedAnnotationClass] = sorted_set.NewSortedSetFn[types.ClassName](nil, types.ClassNameLess) + } + c.annotationProcessorFullQualifiedClassToPluginClass[fullyQualifiedAnnotationClass].Add(processorClass) +} + func equalStringSlices(l, r []string) bool { if len(l) != len(r) { return false diff --git a/java/gazelle/private/java/package.go b/java/gazelle/private/java/package.go index 3defee7a..ba49726e 100644 --- a/java/gazelle/private/java/package.go +++ b/java/gazelle/private/java/package.go @@ -21,7 +21,22 @@ type Package struct { PerClassMetadata map[string]PerClassMetadata } +func (p *Package) AllAnnotations() *sorted_set.SortedSet[types.ClassName] { + annotations := sorted_set.NewSortedSetFn(nil, types.ClassNameLess) + for _, pcm := range p.PerClassMetadata { + annotations.AddAll(pcm.AnnotationClassNames) + for _, method := range pcm.MethodAnnotationClassNames.Keys() { + annotations.AddAll(pcm.MethodAnnotationClassNames.Values(method)) + } + for _, field := range pcm.FieldAnnotationClassNames.Keys() { + annotations.AddAll(pcm.FieldAnnotationClassNames.Values(field)) + } + } + return annotations +} + type PerClassMetadata struct { AnnotationClassNames *sorted_set.SortedSet[types.ClassName] MethodAnnotationClassNames *sorted_multiset.SortedMultiSet[string, types.ClassName] + FieldAnnotationClassNames *sorted_multiset.SortedMultiSet[string, types.ClassName] } diff --git a/java/gazelle/private/javaparser/javaparser.go b/java/gazelle/private/javaparser/javaparser.go index 6218788d..2e07d3e6 100644 --- a/java/gazelle/private/javaparser/javaparser.go +++ b/java/gazelle/private/javaparser/javaparser.go @@ -84,9 +84,20 @@ func (r Runner) ParsePackage(ctx context.Context, in *ParsePackageRequest) (*jav methodAnnotationClassNames.Add(method, *annotationClassName) } } + fieldAnnotationClassNames := sorted_multiset.NewSortedMultiSetFn[string, types.ClassName](types.ClassNameLess) + for field, perField := range v.GetPerFieldMetadata() { + for _, annotation := range perField.AnnotationClassNames { + annotationClassName, err := types.ParseClassName(annotation) + if err != nil { + return nil, fmt.Errorf("failed to parse annotation name %q as a class name in %s: %w", k, annotation, err) + } + fieldAnnotationClassNames.Add(field, *annotationClassName) + } + } metadata := java.PerClassMetadata{ AnnotationClassNames: annotationClassNames, MethodAnnotationClassNames: methodAnnotationClassNames, + FieldAnnotationClassNames: fieldAnnotationClassNames, } perClassMetadata[k] = metadata } diff --git a/java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0/javaparser.proto b/java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0/javaparser.proto index 7d8162f1..876683af 100644 --- a/java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0/javaparser.proto +++ b/java/gazelle/private/javaparser/proto/gazelle/java/javaparser/v0/javaparser.proto @@ -54,12 +54,18 @@ message PerClassMetadata { repeated string annotation_class_names = 1; // Not all methods will be present here, only those with something interesting to report. map per_method_metadata = 2; + // Not all fields will be present here, only those with something interesting to report. + map per_field_metadata = 3; } message PerMethodMetadata { repeated string annotation_class_names = 1; } +message PerFieldMetadata { + repeated string annotation_class_names = 1; +} + service Lifecycle { rpc Shutdown(ShutdownRequest) returns (ShutdownResponse) {} } diff --git a/java/gazelle/private/types/types.go b/java/gazelle/private/types/types.go index ea2d05bf..6a010eca 100644 --- a/java/gazelle/private/types/types.go +++ b/java/gazelle/private/types/types.go @@ -97,6 +97,7 @@ type ResolveInput struct { PackageNames *sorted_set.SortedSet[PackageName] ImportedPackageNames *sorted_set.SortedSet[PackageName] ExportedPackageNames *sorted_set.SortedSet[PackageName] + AnnotationProcessors *sorted_set.SortedSet[ClassName] } type ResolvableJavaPackage struct { diff --git a/java/gazelle/resolve.go b/java/gazelle/resolve.go index a063b1bc..7d0e6c82 100644 --- a/java/gazelle/resolve.go +++ b/java/gazelle/resolve.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "sort" + "strings" "github.com/bazel-contrib/rules_jvm/java/gazelle/javaconfig" "github.com/bazel-contrib/rules_jvm/java/gazelle/private/java" @@ -97,19 +98,13 @@ func (jr *Resolver) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.Re jr.populateAttr(c, packageConfig, r, "deps", resolveInput.ImportedPackageNames, ix, isTestRule, from, resolveInput.PackageNames) jr.populateAttr(c, packageConfig, r, "exports", resolveInput.ExportedPackageNames, ix, isTestRule, from, resolveInput.PackageNames) + + jr.populatePluginsAttr(c, ix, resolveInput, packageConfig, from, isTestRule, r) } func (jr *Resolver) populateAttr(c *config.Config, pc *javaconfig.Config, r *rule.Rule, attrName string, requiredPackageNames *sorted_set.SortedSet[types.PackageName], ix *resolve.RuleIndex, isTestRule bool, from label.Label, ownPackageNames *sorted_set.SortedSet[types.PackageName]) { labels := sorted_set.NewSortedSetFn[label.Label]([]label.Label{}, labelLess) - for _, implicitDep := range r.AttrStrings(attrName) { - l, err := label.Parse(implicitDep) - if err != nil { - panic(fmt.Sprintf("error converting implicit %s %q to label: %v", attrName, implicitDep, err)) - } - labels.Add(l) - } - for _, imp := range requiredPackageNames.SortedSlice() { dep := jr.resolveSinglePackage(c, pc, imp, ix, from, isTestRule, ownPackageNames) if dep == label.NoLabel { @@ -119,18 +114,26 @@ func (jr *Resolver) populateAttr(c *config.Config, pc *javaconfig.Config, r *rul labels.Add(simplifyLabel(c.RepoName, dep, from)) } - var exprs []build.Expr - if labels.Len() > 0 { - for _, l := range labels.SortedSlice() { - if l.Relative && l.Name == from.Name { - continue - } - exprs = append(exprs, &build.StringExpr{Value: l.String()}) + setLabelAttrIncludingExistingValues(r, attrName, labels) +} + +func (jr *Resolver) populatePluginsAttr(c *config.Config, ix *resolve.RuleIndex, resolveInput types.ResolveInput, packageConfig *javaconfig.Config, from label.Label, isTestRule bool, r *rule.Rule) { + pluginLabels := sorted_set.NewSortedSetFn[label.Label]([]label.Label{}, labelLess) + for _, annotationProcessor := range resolveInput.AnnotationProcessors.SortedSlice() { + dep := jr.resolveSinglePackage(c, packageConfig, annotationProcessor.PackageName(), ix, from, isTestRule, resolveInput.PackageNames) + if dep == label.NoLabel { + continue } + + // Use the naming scheme for plugins as per https://github.com/bazelbuild/rules_jvm_external/pull/1102 + // In the case of overrides (i.e. # gazelle:resolve targets) we require that they follow the same name-mangling scheme for the java_plugin target as rules_jvm_external uses. + // Ideally this would be a call to `java_plugin_artifact(dep.String(), annotationProcessor.FullyQualifiedClassName())` but we don't have function calls working in attributes. + dep.Name += "__java_plugin__" + strings.NewReplacer(".", "_", "$", "_").Replace(annotationProcessor.FullyQualifiedClassName()) + + pluginLabels.Add(simplifyLabel(c.RepoName, dep, from)) } - if len(exprs) > 0 { - r.SetAttr(attrName, exprs) - } + + setLabelAttrIncludingExistingValues(r, "plugins", pluginLabels) } func labelLess(l, r label.Label) bool { @@ -159,6 +162,30 @@ func simplifyLabel(repoName string, l label.Label, from label.Label) label.Label return l } +// Note: This function may modify labels. +func setLabelAttrIncludingExistingValues(r *rule.Rule, attrName string, labels *sorted_set.SortedSet[label.Label]) { + for _, implicitDep := range r.AttrStrings(attrName) { + l, err := label.Parse(implicitDep) + if err != nil { + panic(fmt.Sprintf("error converting implicit %s %q to label: %v", attrName, implicitDep, err)) + } + labels.Add(l) + } + + var exprs []build.Expr + if labels.Len() > 0 { + for _, l := range labels.SortedSlice() { + if l.Relative && l.Name == r.Name() { + continue + } + exprs = append(exprs, &build.StringExpr{Value: l.String()}) + } + } + if len(exprs) > 0 { + r.SetAttr(attrName, exprs) + } +} + func (jr *Resolver) resolveSinglePackage(c *config.Config, pc *javaconfig.Config, imp types.PackageName, ix *resolve.RuleIndex, from label.Label, isTestRule bool, ownPackageNames *sorted_set.SortedSet[types.PackageName]) (out label.Label) { cacheKey := types.NewResolvableJavaPackage(imp, false, false) importSpec := resolve.ImportSpec{Lang: languageName, Imp: cacheKey.String()} diff --git a/java/gazelle/testdata/annotation_processor/BUILD.in b/java/gazelle/testdata/annotation_processor/BUILD.in new file mode 100644 index 00000000..0dbbc2cf --- /dev/null +++ b/java/gazelle/testdata/annotation_processor/BUILD.in @@ -0,0 +1 @@ +# gazelle:java_annotation_processor_plugin com.google.auto.value.AutoValue com.google.auto.value.processor.AutoValueProcessor diff --git a/java/gazelle/testdata/annotation_processor/WORKSPACE b/java/gazelle/testdata/annotation_processor/WORKSPACE new file mode 100644 index 00000000..a5cd8583 --- /dev/null +++ b/java/gazelle/testdata/annotation_processor/WORKSPACE @@ -0,0 +1,38 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +RULES_JVM_EXTERNAL_TAG = "6.1" + +RULES_JVM_EXTERNAL_SHA = "08ea921df02ffe9924123b0686dc04fd0ff875710bfadb7ad42badb931b0fd50" + +http_archive( + name = "rules_jvm_external", + sha256 = RULES_JVM_EXTERNAL_SHA, + strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG, + url = "https://github.com/bazelbuild/rules_jvm_external/releases/download/%s/rules_jvm_external-%s.tar.gz" % (RULES_JVM_EXTERNAL_TAG, RULES_JVM_EXTERNAL_TAG), +) + +load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps") + +rules_jvm_external_deps() + +load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup") + +rules_jvm_external_setup() + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + name = "maven", + artifacts = [ + "com.google.auto.value:auto-value:1.10.4", + "com.google.auto.value:auto-value-annotations:1.10.4", + ], + maven_install_json = "@//:maven_install.json", + repositories = [ + "https://repo1.maven.org/maven2", + ], +) + +load("@maven//:defs.bzl", "pinned_maven_install") + +pinned_maven_install() diff --git a/java/gazelle/testdata/annotation_processor/maven_install.json b/java/gazelle/testdata/annotation_processor/maven_install.json new file mode 100644 index 00000000..1ee5d42d --- /dev/null +++ b/java/gazelle/testdata/annotation_processor/maven_install.json @@ -0,0 +1,138 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": -702707572, + "__RESOLVED_ARTIFACTS_HASH": 1474543296, + "artifacts": { + "com.google.auto.value:auto-value": { + "shasums": { + "jar": "f3c438d1f82904bbcb452084d488b660f3c7488e9274c3a58f049e121632d434" + }, + "version": "1.10.4" + }, + "com.google.auto.value:auto-value-annotations": { + "shasums": { + "jar": "e1c45e6beadaef9797cb0d9afd5a45621ad061cd8632012f85582853a3887825" + }, + "version": "1.10.4" + } + }, + "dependencies": {}, + "packages": { + "com.google.auto.value:auto-value": [ + "autovalue.shaded.com.google.auto.common", + "autovalue.shaded.com.google.auto.service", + "autovalue.shaded.com.google.common.annotations", + "autovalue.shaded.com.google.common.base", + "autovalue.shaded.com.google.common.cache", + "autovalue.shaded.com.google.common.collect", + "autovalue.shaded.com.google.common.escape", + "autovalue.shaded.com.google.common.eventbus", + "autovalue.shaded.com.google.common.graph", + "autovalue.shaded.com.google.common.hash", + "autovalue.shaded.com.google.common.html", + "autovalue.shaded.com.google.common.io", + "autovalue.shaded.com.google.common.math", + "autovalue.shaded.com.google.common.net", + "autovalue.shaded.com.google.common.primitives", + "autovalue.shaded.com.google.common.reflect", + "autovalue.shaded.com.google.common.util.concurrent", + "autovalue.shaded.com.google.common.xml", + "autovalue.shaded.com.google.errorprone.annotations", + "autovalue.shaded.com.google.errorprone.annotations.concurrent", + "autovalue.shaded.com.google.escapevelocity", + "autovalue.shaded.com.google.j2objc.annotations", + "autovalue.shaded.com.squareup.javapoet", + "autovalue.shaded.kotlin", + "autovalue.shaded.kotlin.annotation", + "autovalue.shaded.kotlin.collections", + "autovalue.shaded.kotlin.collections.builders", + "autovalue.shaded.kotlin.collections.unsigned", + "autovalue.shaded.kotlin.comparisons", + "autovalue.shaded.kotlin.contracts", + "autovalue.shaded.kotlin.coroutines", + "autovalue.shaded.kotlin.coroutines.intrinsics", + "autovalue.shaded.kotlin.coroutines.jvm.internal", + "autovalue.shaded.kotlin.enums", + "autovalue.shaded.kotlin.experimental", + "autovalue.shaded.kotlin.internal", + "autovalue.shaded.kotlin.internal.jdk7", + "autovalue.shaded.kotlin.internal.jdk8", + "autovalue.shaded.kotlin.jvm", + "autovalue.shaded.kotlin.jvm.functions", + "autovalue.shaded.kotlin.jvm.internal", + "autovalue.shaded.kotlin.jvm.internal.markers", + "autovalue.shaded.kotlin.random", + "autovalue.shaded.kotlin.random.jdk8", + "autovalue.shaded.kotlin.ranges", + "autovalue.shaded.kotlin.reflect", + "autovalue.shaded.kotlin.sequences", + "autovalue.shaded.kotlin.text", + "autovalue.shaded.kotlinx.metadata", + "autovalue.shaded.kotlinx.metadata.internal", + "autovalue.shaded.kotlinx.metadata.internal.common", + "autovalue.shaded.kotlinx.metadata.internal.extensions", + "autovalue.shaded.kotlinx.metadata.internal.metadata", + "autovalue.shaded.kotlinx.metadata.internal.metadata.deserialization", + "autovalue.shaded.kotlinx.metadata.internal.metadata.jvm", + "autovalue.shaded.kotlinx.metadata.internal.metadata.jvm.deserialization", + "autovalue.shaded.kotlinx.metadata.internal.metadata.jvm.serialization", + "autovalue.shaded.kotlinx.metadata.internal.metadata.serialization", + "autovalue.shaded.kotlinx.metadata.internal.protobuf", + "autovalue.shaded.kotlinx.metadata.jvm", + "autovalue.shaded.kotlinx.metadata.jvm.internal", + "autovalue.shaded.net.ltgt.gradle.incap", + "autovalue.shaded.org.checkerframework.checker.nullness.qual", + "autovalue.shaded.org.checkerframework.framework.qual", + "autovalue.shaded.org.jetbrains.annotations", + "autovalue.shaded.org.objectweb.asm", + "com.google.auto.value.extension", + "com.google.auto.value.extension.memoized.processor", + "com.google.auto.value.extension.serializable.processor", + "com.google.auto.value.extension.serializable.serializer", + "com.google.auto.value.extension.serializable.serializer.impl", + "com.google.auto.value.extension.serializable.serializer.interfaces", + "com.google.auto.value.extension.serializable.serializer.runtime", + "com.google.auto.value.extension.toprettystring.processor", + "com.google.auto.value.processor" + ], + "com.google.auto.value:auto-value-annotations": [ + "com.google.auto.value", + "com.google.auto.value.extension.memoized", + "com.google.auto.value.extension.serializable", + "com.google.auto.value.extension.toprettystring" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.google.auto.value:auto-value", + "com.google.auto.value:auto-value-annotations" + ] + }, + "services": { + "com.google.auto.value:auto-value": { + "autovalue.shaded.kotlinx.metadata.internal.extensions.MetadataExtensions": [ + "autovalue.shaded.kotlinx.metadata.jvm.internal.JvmMetadataExtensions" + ], + "com.google.auto.value.extension.AutoValueExtension": [ + "com.google.auto.value.extension.memoized.processor.MemoizeExtension", + "com.google.auto.value.extension.serializable.processor.SerializableAutoValueExtension", + "com.google.auto.value.extension.toprettystring.processor.ToPrettyStringExtension" + ], + "com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension": [ + "com.google.auto.value.extension.serializable.serializer.impl.ImmutableListSerializerExtension", + "com.google.auto.value.extension.serializable.serializer.impl.ImmutableMapSerializerExtension", + "com.google.auto.value.extension.serializable.serializer.impl.OptionalSerializerExtension" + ], + "javax.annotation.processing.Processor": [ + "com.google.auto.value.extension.memoized.processor.MemoizedValidator", + "com.google.auto.value.extension.toprettystring.processor.ToPrettyStringValidator", + "com.google.auto.value.processor.AutoAnnotationProcessor", + "com.google.auto.value.processor.AutoBuilderProcessor", + "com.google.auto.value.processor.AutoOneOfProcessor", + "com.google.auto.value.processor.AutoValueBuilderProcessor", + "com.google.auto.value.processor.AutoValueProcessor" + ] + } + }, + "version": "2" +} diff --git a/java/gazelle/testdata/annotation_processor/src/main/java/com/example/BUILD.out b/java/gazelle/testdata/annotation_processor/src/main/java/com/example/BUILD.out new file mode 100644 index 00000000..0d61c04e --- /dev/null +++ b/java/gazelle/testdata/annotation_processor/src/main/java/com/example/BUILD.out @@ -0,0 +1,16 @@ +load("@rules_java//java:defs.bzl", "java_binary", "java_library") + +java_library( + name = "example", + srcs = ["Main.java"], + plugins = ["@maven//:com_google_auto_value_auto_value__java_plugin__com_google_auto_value_processor_AutoValueProcessor"], + visibility = ["//:__subpackages__"], + deps = ["@maven//:com_google_auto_value_auto_value_annotations"], +) + +java_binary( + name = "Main", + main_class = "com.example.Main", + visibility = ["//visibility:public"], + runtime_deps = [":example"], +) diff --git a/java/gazelle/testdata/annotation_processor/src/main/java/com/example/Main.java b/java/gazelle/testdata/annotation_processor/src/main/java/com/example/Main.java new file mode 100644 index 00000000..3ea1ad03 --- /dev/null +++ b/java/gazelle/testdata/annotation_processor/src/main/java/com/example/Main.java @@ -0,0 +1,21 @@ +package com.example; + +import com.google.auto.value.AutoValue; + +class Main { + public static void main(String[] args) { + Animal pig = Animal.create("pig", 4); + Animal chicken = Animal.create("chicken", 2); + System.out.printf("Checking if %s has same legs as %s: %s%n", pig, chicken, pig.numberOfLegs() == chicken.numberOfLegs()); + } + + @AutoValue + public abstract static class Animal { + static Animal create(String name, int numberOfLegs) { + return new AutoValue_Main_Animal(name, numberOfLegs); + } + + abstract String name(); + abstract int numberOfLegs(); + } +} diff --git a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParser.java b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParser.java index efa911a3..c2e25abd 100644 --- a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParser.java +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParser.java @@ -16,6 +16,7 @@ import com.sun.source.tree.ImportTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.MethodTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.PackageTree; import com.sun.source.tree.ParameterizedTypeTree; @@ -31,6 +32,7 @@ import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -73,7 +75,7 @@ public ClasspathParser() { static class PerClassData { public PerClassData() { - this(new TreeSet<>(), new TreeMap<>()); + this(new TreeSet<>(), new TreeMap<>(), new TreeMap<>()); } @Override @@ -83,18 +85,24 @@ public String toString() { + annotations + ", perMethodAnnotations=" + perMethodAnnotations + + ", perFieldAnnotations=" + + perFieldAnnotations + '}'; } public PerClassData( - SortedSet annotations, SortedMap> perMethodAnnotations) { + SortedSet annotations, + SortedMap> perMethodAnnotations, + SortedMap> perFieldAnnotations) { this.annotations = annotations; this.perMethodAnnotations = perMethodAnnotations; + this.perFieldAnnotations = perFieldAnnotations; } final SortedSet annotations; final SortedMap> perMethodAnnotations; + final SortedMap> perFieldAnnotations; @Override public boolean equals(Object o) { @@ -102,12 +110,13 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; PerClassData that = (PerClassData) o; return Objects.equals(annotations, that.annotations) - && Objects.equals(perMethodAnnotations, that.perMethodAnnotations); + && Objects.equals(perMethodAnnotations, that.perMethodAnnotations) + && Objects.equals(perFieldAnnotations, that.perFieldAnnotations); } @Override public int hashCode() { - return Objects.hash(annotations, perMethodAnnotations); + return Objects.hash(annotations, perMethodAnnotations, perFieldAnnotations); } } @@ -178,7 +187,8 @@ class ClassScanner extends TreeScanner { @Nullable private String currentPackage; // Stack of possibly-nested contexts we may currently be in. - // First element is the outer-most context (e.g. top-level class), last element is the inner-most context (e.g. inner class). + // First element is the outer-most context (e.g. top-level class), last element is the + // inner-most context (e.g. inner class). // Currently tracks classes, so that we can know what outer and inner classes we may be in. private final Deque stack = new ArrayDeque<>(); @@ -187,7 +197,8 @@ class ClassScanner extends TreeScanner { void popOrThrow(Tree expected) { Tree popped = stack.removeLast(); if (!expected.equals(popped)) { - throw new IllegalStateException(String.format("Expected to pop %s but got %s", expected, popped)); + throw new IllegalStateException( + String.format("Expected to pop %s but got %s", expected, popped)); } } @@ -256,6 +267,7 @@ public Void visitClass(ClassTree t, Void v) { @Override public Void visitMethod(com.sun.source.tree.MethodTree m, Void v) { + stack.addLast(m); boolean isVoidReturn = false; // Check the return type on the method. @@ -303,7 +315,9 @@ public Void visitMethod(com.sun.source.tree.MethodTree m, Void v) { } } - return super.visitMethod(m, v); + Void ret = super.visitMethod(m, v); + popOrThrow(m); + return ret; } private void handleAnnotations(List annotations) { @@ -356,6 +370,23 @@ public Void visitVariable(VariableTree node, Void unused) { if (node.getType() != null) { checkFullyQualifiedType(node.getType()); } + + // Local variables inside methods shouldn't be treated as fields. + if (isDirectlyInClass()) { + for (AnnotationTree annotation : node.getModifiers().getAnnotations()) { + String annotationClassName = annotation.getAnnotationType().toString(); + String importedFullyQualified = currentFileImports.get(annotationClassName); + String currentFullyQualifiedClass = currentFullyQualifiedClassName(); + if (importedFullyQualified != null) { + noteAnnotatedField( + currentFullyQualifiedClass, node.getName().toString(), importedFullyQualified); + } else { + noteAnnotatedField( + currentFullyQualifiedClass, node.getName().toString(), annotationClassName); + } + } + } + return super.visitVariable(node, unused); } @@ -388,6 +419,20 @@ private Set checkFullyQualifiedType(Tree identifier) { return types; } + private boolean isDirectlyInClass() { + Iterator treeIterator = stack.descendingIterator(); + while (treeIterator.hasNext()) { + Tree tree = treeIterator.next(); + if (tree instanceof ClassTree) { + return true; + } + if (tree instanceof MethodTree) { + return false; + } + } + return false; + } + @Nullable private String currentNestedClassNameWithoutPackage() { List parts = new ArrayList<>(); @@ -443,4 +488,18 @@ private void noteAnnotatedMethod( } data.perMethodAnnotations.get(methodName).add(annotationFullyQualifiedClassName); } + + private void noteAnnotatedField( + String annotatedFullyQualifiedClassName, + String fieldName, + String annotationFullyQualifiedClassName) { + if (!perClassData.containsKey(annotatedFullyQualifiedClassName)) { + perClassData.put(annotatedFullyQualifiedClassName, new PerClassData()); + } + PerClassData data = perClassData.get(annotatedFullyQualifiedClassName); + if (!data.perFieldAnnotations.containsKey(fieldName)) { + data.perFieldAnnotations.put(fieldName, new TreeSet<>()); + } + data.perFieldAnnotations.get(fieldName).add(annotationFullyQualifiedClassName); + } } diff --git a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/GrpcServer.java b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/GrpcServer.java index 660754c7..62f85c7b 100644 --- a/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/GrpcServer.java +++ b/java/src/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/GrpcServer.java @@ -7,6 +7,7 @@ import com.gazelle.java.javaparser.v0.Package.Builder; import com.gazelle.java.javaparser.v0.ParsePackageRequest; import com.gazelle.java.javaparser.v0.PerClassMetadata; +import com.gazelle.java.javaparser.v0.PerFieldMetadata; import com.gazelle.java.javaparser.v0.PerMethodMetadata; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableSet; @@ -168,6 +169,14 @@ private Package getImports(ParsePackageRequest request) { .addAllAnnotationClassNames(methodEntry.getValue()) .build()); } + for (Map.Entry> fieldEntry : + classEntry.getValue().perFieldAnnotations.entrySet()) { + perClassMetadata.putPerFieldMetadata( + fieldEntry.getKey(), + PerFieldMetadata.newBuilder() + .addAllAnnotationClassNames(fieldEntry.getValue()) + .build()); + } packageBuilder.putPerClassMetadata(classEntry.getKey(), perClassMetadata.build()); } return packageBuilder.build(); diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParserTest.java b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParserTest.java index 1eef75e1..c8be7ded 100644 --- a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParserTest.java +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/ClasspathParserTest.java @@ -201,23 +201,59 @@ public void testAnnotationAfterImport() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationAfterImport", - new ClasspathParser.PerClassData(treeSet("com.example.FlakyTest"), new TreeMap<>())), + new ClasspathParser.PerClassData( + treeSet("com.example.FlakyTest"), new TreeMap<>(), new TreeMap<>())), parser.perClassData); } @Test public void testAnnotationAfterImportOnNestedClass() throws IOException { List files = - List.of( - testFiles.get( - "/workspace/com/gazelle/java/javaparser/generators/NestedClassAnnotations.java")); + List.of( + testFiles.get( + "/workspace/com/gazelle/java/javaparser/generators/NestedClassAnnotations.java")); parser.parseClasses(files); assertEquals( - Map.of( - "workspace.com.gazelle.java.javaparser.generators.NestedClassAnnotations.Inner", - new ClasspathParser.PerClassData(treeSet("com.example.FlakyTest"), new TreeMap<>())), - parser.perClassData); + Map.of( + "workspace.com.gazelle.java.javaparser.generators.NestedClassAnnotations.Inner", + new ClasspathParser.PerClassData( + treeSet("com.example.FlakyTest"), new TreeMap<>(), new TreeMap<>())), + parser.perClassData); + } + + @Test + public void testAnnotationOnField() throws IOException { + List files = + List.of( + testFiles.get( + "/workspace/com/gazelle/java/javaparser/generators/AnnotationOnField.java")); + parser.parseClasses(files); + + TreeMap> expectedOuterClassFieldAnnotations = new TreeMap<>(); + expectedOuterClassFieldAnnotations.put("someField", treeSet("lombok.Getter")); + + TreeMap> expectedInnerClassFieldAnnotations = new TreeMap<>(); + expectedInnerClassFieldAnnotations.put("canBeSet", treeSet("lombok.Setter")); + + TreeMap> expectedInnerEnumFieldAnnotations = new TreeMap<>(); + expectedInnerEnumFieldAnnotations.put("size", treeSet("lombok.Getter")); + + TreeMap expected = new TreeMap<>(); + expected.put( + "workspace.com.gazelle.java.javaparser.generators.AnnotationOnField", + new ClasspathParser.PerClassData( + new TreeSet<>(), new TreeMap<>(), expectedOuterClassFieldAnnotations)); + expected.put( + "workspace.com.gazelle.java.javaparser.generators.AnnotationOnField.InnerClass", + new ClasspathParser.PerClassData( + new TreeSet<>(), new TreeMap<>(), expectedInnerClassFieldAnnotations)); + expected.put( + "workspace.com.gazelle.java.javaparser.generators.AnnotationOnField.InnerEnum", + new ClasspathParser.PerClassData( + new TreeSet<>(), new TreeMap<>(), expectedInnerEnumFieldAnnotations)); + + assertEquals(expected, parser.perClassData); } @Test @@ -234,7 +270,8 @@ public void testAnnotationAfterImportOnMethod() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationAfterImportOnMethod", - new ClasspathParser.PerClassData(new TreeSet<>(), expectedPerMethodAnnotations)), + new ClasspathParser.PerClassData( + new TreeSet<>(), expectedPerMethodAnnotations, new TreeMap<>())), parser.perClassData); } @@ -251,7 +288,8 @@ public void testAnnotationFromJavaStandardLibrary() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationFromJavaStandardLibrary", - new ClasspathParser.PerClassData(treeSet("Deprecated"), new TreeMap<>())), + new ClasspathParser.PerClassData( + treeSet("Deprecated"), new TreeMap<>(), new TreeMap<>())), parser.perClassData); } @@ -268,7 +306,8 @@ public void testAnnotationWithoutImport() throws IOException { assertEquals( Map.of( "workspace.com.gazelle.java.javaparser.generators.AnnotationWithoutImport", - new ClasspathParser.PerClassData(treeSet("WhoKnowsWhereIAmFrom"), new TreeMap<>())), + new ClasspathParser.PerClassData( + treeSet("WhoKnowsWhereIAmFrom"), new TreeMap<>(), new TreeMap<>())), parser.perClassData); } diff --git a/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/java/javaparser/generators/AnnotationOnField.java b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/java/javaparser/generators/AnnotationOnField.java new file mode 100644 index 00000000..3762ad34 --- /dev/null +++ b/java/test/com/github/bazel_contrib/contrib_rules_jvm/javaparser/generators/workspace/com/gazelle/java/javaparser/generators/AnnotationOnField.java @@ -0,0 +1,26 @@ +package workspace.com.gazelle.java.javaparser.generators; + +import lombok.Getter; +import lombok.Setter; +import com.example.NonNull; + +public class AnnotationOnField { + @Getter + private final String someField; + + public void doSomething() { + @NonNull String variable = "hello"; + } + + private static class InnerClass { + @Setter + private String canBeSet; + } + + public enum InnerEnum { + VARIANT; + + @Getter + private final int size; + } +}