2424import org .openrewrite .java .JavaVisitor ;
2525import org .openrewrite .java .NoMissingTypes ;
2626import org .openrewrite .java .service .AnnotationService ;
27- import org .openrewrite .java .tree .J ;
28- import org .openrewrite .java .tree .Space ;
29- import org .openrewrite .java .tree .Statement ;
30- import org .openrewrite .java .tree .TypeUtils ;
27+ import org .openrewrite .java .tree .*;
3128
3229import java .util .*;
3330import java .util .concurrent .atomic .AtomicBoolean ;
@@ -59,7 +56,8 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
5956 class CheckField {
6057 J .VariableDeclarations declarations ;
6158
62- @ Nullable Statement nextStatement ;
59+ @ Nullable
60+ Statement nextStatement ;
6361 }
6462
6563 @ Override
@@ -75,7 +73,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
7573 List <CheckField > checkFields = new ArrayList <>();
7674 // Do not remove fields with `serialVersionUID` name.
7775 boolean skipSerialVersionUID = cd .getType () == null ||
78- cd .getType ().isAssignableTo ("java.io.Serializable" );
76+ cd .getType ().isAssignableTo ("java.io.Serializable" );
7977
8078 List <Statement > statements = cd .getBody ().getStatements ();
8179 for (int i = 0 ; i < statements .size (); i ++) {
@@ -84,8 +82,8 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
8482 J .VariableDeclarations vd = (J .VariableDeclarations ) statement ;
8583 // RSPEC-S1068 does not apply serialVersionUID of Serializable classes, or fields with annotations.
8684 if (!(skipSerialVersionUID && isSerialVersionUid (vd )) &&
87- vd .getLeadingAnnotations ().isEmpty () &&
88- vd .hasModifier (J .Modifier .Type .Private )) {
85+ vd .getLeadingAnnotations ().isEmpty () &&
86+ vd .hasModifier (J .Modifier .Type .Private )) {
8987 Statement nextStatement = i < statements .size () - 1 ? statements .get (i + 1 ) : null ;
9088 checkFields .add (new CheckField (vd , nextStatement ));
9189 }
@@ -133,10 +131,10 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
133131
134132 private boolean isSerialVersionUid (J .VariableDeclarations vd ) {
135133 return vd .hasModifier (J .Modifier .Type .Private ) &&
136- vd .hasModifier (J .Modifier .Type .Static ) &&
137- vd .hasModifier (J .Modifier .Type .Final ) &&
138- TypeUtils .isOfClassType (vd .getType (), "long" ) &&
139- vd .getVariables ().stream ().anyMatch (it -> "serialVersionUID" .equals (it .getSimpleName ()));
134+ vd .hasModifier (J .Modifier .Type .Static ) &&
135+ vd .hasModifier (J .Modifier .Type .Final ) &&
136+ TypeUtils .isOfClassType (vd .getType (), "long" ) &&
137+ vd .getVariables ().stream ().anyMatch (it -> "serialVersionUID" .equals (it .getSimpleName ()));
140138 }
141139 };
142140 return Preconditions .check (new NoMissingTypes (), Repeat .repeatUntilStable (visitor ));
@@ -146,12 +144,15 @@ private static class VariableUses {
146144 public static Map <J .VariableDeclarations .NamedVariable , List <J .Identifier >> find (J .VariableDeclarations declarations , J .ClassDeclaration parent ) {
147145 Map <J .VariableDeclarations .NamedVariable , List <J .Identifier >> found = new IdentityHashMap <>(declarations .getVariables ().size ());
148146 Map <String , J .VariableDeclarations .NamedVariable > signatureMap = new HashMap <>();
147+ Map <String , J .VariableDeclarations .NamedVariable > nameMap = new HashMap <>();
149148
150149 for (J .VariableDeclarations .NamedVariable variable : declarations .getVariables ()) {
151150 if (variable .getVariableType () != null ) {
152151 found .computeIfAbsent (variable , k -> new ArrayList <>());
153152 // Note: Using a variable type signature is only safe to find uses of class fields.
154153 signatureMap .put (variable .getVariableType ().toString (), variable );
154+ // Also map by name for fallback matching
155+ nameMap .put (variable .getSimpleName (), variable );
155156 }
156157 }
157158
@@ -161,17 +162,36 @@ public static Map<J.VariableDeclarations.NamedVariable, List<J.Identifier>> find
161162 @ Override
162163 public J .Identifier visitIdentifier (J .Identifier identifier ,
163164 Map <J .VariableDeclarations .NamedVariable , List <J .Identifier >> identifiers ) {
164- if (identifier .getFieldType () != null && signatureMap .containsKey (identifier .getFieldType ().toString ())) {
165- Cursor parent = getCursor ().dropParentUntil (is ->
166- is instanceof J .VariableDeclarations ||
167- is instanceof J .ClassDeclaration );
168-
169- if (!(parent .getValue () instanceof J .VariableDeclarations && parent .getValue () == declarations )) {
170- J .VariableDeclarations .NamedVariable name = signatureMap .get (identifier .getFieldType ().toString ());
171- if (declarations .getVariables ().contains (name )) {
172- J .VariableDeclarations .NamedVariable used = signatureMap .get (identifier .getFieldType ().toString ());
173- identifiers .computeIfAbsent (used , k -> new ArrayList <>())
174- .add (identifier );
165+ if (identifier .getFieldType () != null ) {
166+ J .VariableDeclarations .NamedVariable match = null ;
167+
168+ // First try exact type signature match
169+ String fieldTypeSignature = identifier .getFieldType ().toString ();
170+ if (signatureMap .containsKey (fieldTypeSignature )) {
171+ match = signatureMap .get (fieldTypeSignature );
172+ } else if (identifier .getFieldType ().getOwner () != null ) {
173+ // Fallback: match by name if it's a field reference from the same class
174+ J .VariableDeclarations .NamedVariable nameMatch = nameMap .get (identifier .getSimpleName ());
175+ if (nameMatch != null && nameMatch .getVariableType () != null ) {
176+ // Check if the owner type matches the parent class type
177+ JavaType .FullyQualified ownerType = TypeUtils .asFullyQualified (identifier .getFieldType ().getOwner ());
178+ JavaType .FullyQualified parentType = parent .getType ();
179+ if (ownerType != null && parentType != null &&
180+ TypeUtils .fullyQualifiedNamesAreEqual (ownerType .getFullyQualifiedName (), parentType .getFullyQualifiedName ())) {
181+ match = nameMatch ;
182+ }
183+ }
184+ }
185+
186+ if (match != null ) {
187+ Cursor parentCursor = getCursor ().dropParentUntil (is ->
188+ is instanceof J .VariableDeclarations || is instanceof J .ClassDeclaration );
189+
190+ if (!(parentCursor .getValue () instanceof J .VariableDeclarations && parentCursor .getValue () == declarations )) {
191+ if (declarations .getVariables ().contains (match )) {
192+ identifiers .computeIfAbsent (match , k -> new ArrayList <>())
193+ .add (identifier );
194+ }
175195 }
176196 }
177197 }
0 commit comments