rewrite icon indicating copy to clipboard operation
rewrite copied to clipboard

ShortenFullyQualifiedTypeReferences conflicts with Types defined in parent class

Open mbruggmann opened this issue 1 year ago • 1 comments

What version of OpenRewrite are you using?

rewrite-java 8.31.1

What is the smallest, simplest way to reproduce the problem?

I can reproduce the issue using this test case:

  @Test
  void leaveFullTypeReferenceForSuperclassClash() {
    rewriteRun(
        spec -> spec.recipe(new ShortenFullyQualifiedTypeReferences()),
        srcMainJava(
            java(
                """
                package com.helloworld;

                public interface Parent {
                  class Set {}
                }"""),
            java(
                """
                package com.helloworld;

                public class Main implements Parent {
                  public void get() {
                    final java.util.Set set = null;
                  }
                }""")));
  }

What did you expect to see?

Because a class definition in the parent interface with the same type name shadows the import, the fully qualified type reference is still needed. I would expect the recipe to bail out in this case.

What did you see instead?

  package com.helloworld;
  
  import java.util.Set;
  
  public class Main implements Parent {
    public void get() {
      final Set set = null;  // <-- this variable now became Parent.Set instead of java.util.Set
    }
  }

Are you interested in contributing a fix to OpenRewrite?

Short on time at the moment, possibly later.

mbruggmann avatar Aug 02 '24 09:08 mbruggmann

This looks tricky to fix. The problem is that we in the type attribution only have the reference from a nested class to the outer class, but not the inverse. This is something we should look into to allow this recipe to be implemented correctly.

knutwannheden avatar Aug 25 '24 05:08 knutwannheden

I encountered a very similar issue in which we had something along these lines:

import org.example.v1.UnrelatedType1;
import org.example.v1.SomethingElse2;
import org.example.v360.Type1;
import org.example.v360.Type2;
import org.example.v360.Type3;

[ .. and then a lot of methods using things like org.example.v1.Type3 / 4 / ... ]

Let's assume both v1 & v360 have Role defined.

However, we had (in this case) a test defined like:

        when(roleMapper.mapRole(any(java.util.ArrayList.class))).thenReturn(new ArrayList<org.example.v1.Role>());

As you can spot, Role is not imported, but referenced directly. (as were a lot of other things -- bad I know)

Autoconversion made it into:

import org.example.v1.*;
import org.example.v360.*;

        when(roleMapper.mapRole(any(ArrayList.class))).thenReturn(new ArrayList<Role>());

But now Role is not uniquely defined any longer.

I think this is a play between ShortenFullyQualifiedTypeReferences and org.openrewrite.staticanalysis.CodeCleanup or org.openrewrite.java.OrderImports.

If this is sufficiently different from OP's issue, I'll copy/paste this explanation into a new ticket. Just tell me if I need to do so.

svaningelgem avatar Dec 16 '24 13:12 svaningelgem