groovy-eclipse icon indicating copy to clipboard operation
groovy-eclipse copied to clipboard

Visibility errors when recompiling: "cannot override ... attempting to assign weaker access privileges; was public"

Open kriegaex opened this issue 10 years ago • 14 comments

Lately I am seeing compile errors with my Spock/Geb classes when overriding methods like setup() or cleanup() across three levels of class hierarchy:

cannot override setup() ... attempting to assign weaker access privileges; was public

The errors only occur when doing an incremental recompile, full rebuild works. They go away when I manually recompile the base class.

All methods were defined like this without calling their parent methods because Spock calls each setup method in a defined order anyway:

def setup() { ... }

Environment:

  • IntelliJ IDEA 15.0.4 (commercial version)
  • groovy-eclipse-batch-2.4.3-01.jar as Java & Groovy compiler
  • OpenJDK Runtime Environment (Zulu 8.13.0.5-win64) (build 1.8.0_72-b15)

@aclement: This seems to be similar to https://github.com/grails/grails-core/issues/619, an issue you already know.

kriegaex avatar Apr 04 '16 06:04 kriegaex

I was just trying to recreate a minimal test case here in IntelliJ using Geb. I saw the error twice while applying little changes to my code hierarchy, but have not managed to come up with a clear, repeatable list of instructions to reproduce it. I guess it had to do with a base class method def setup(), implicitly resulting in an equivalent to public Object setup(). Then I had a few subclasses, each overriding the method. Because of automatic code generation by my IDE, I happened to override the methods as void setup(). Then at one time when I noticed and also changed it to def setup(), the error occurred and was only healed by either manually recompiling the changed class or doing a full rebuild.

If that gives you any clue as to what could be the reason and how I could go about reproducing it in a repeatable way, I would be grateful.

kriegaex avatar Feb 01 '17 22:02 kriegaex

The base type that contains the setup method, it it an interface, a trait or anything like that? Does it have AST transforms applied? Does your extending class have transforms?

eric-milles avatar Feb 02 '17 18:02 eric-milles

Oops, now you got me. I have no idea what AST transform means in this context. I do know what an AST is, though. It is very well possible that Spock and/or Geb do something like that, doing magic with the "given-when-then-where" DSL Spock provides. I thought you Groovy guys know better than I. I am merely a user who loves to use Spock and Geb for all things test automation. I am a bit schizophrenic here: My application classes are 100% Java, my test classes 100% Groovy. ;-)

Here is my test case:

package de.scrum_master.tdd

import geb.spock.GebReportingSpec

class MyBaseSpec extends GebReportingSpec {
  def setup() {
    println "Base setup"
  }
}
package de.scrum_master.tdd

class MySubSpec extends MyBaseSpec {
  def setup() {
    println "Sub setup"
  }
}
package de.scrum_master.tdd

class SampleGebIT extends MySubSpec {
  def "Open Scrum-Master.de"() {
    given:
    go "https://scrum-master.de"
    report "Scrum-Master.de"

    expect:
    $("h2").text() == "Herzlich Willkommen bei Scrum-Master.de"
  }

  def setup() {
    println "Sample setup"
  }
}

And BTW, the class hierarchy looks like this: image

The original definition for setup() is in upstream Geb class GebReportingSpec:

class GebReportingSpec extends GebSpec {
    // (...)
    def setup() {
        reportGroup getClass()
    }
    // (...)
}

Update: BTW, the console log says (after successful compilation):

Running de.scrum_master.tdd.SampleGebIT
Base setup
Sub setup
Sample setup

As you can see, there is some magic because in Spock/Geb always all setup() or cleanup() methods are executed bottom-up from subclass to base classes. There is no need to @Override or call super().

kriegaex avatar Feb 02 '17 20:02 kriegaex

Aha, yes. Spock adds JUnit 4 @Before to all the setup() methods. Not sure what else it might do. I'll try recreating in a Spock context and see what I can see.

eric-milles avatar Feb 02 '17 21:02 eric-milles

I was able to recreate this one by adding Spock's global transform to the editor: -Dgreclipse.globalTransformsInReconcile=org.spockframework.compiler.SpockTransform

spock-error

eric-milles avatar Nov 08 '17 22:11 eric-milles

Error is logged from org.codehaus.groovy.classgen.ClassCompletionVerifier. At this point, mn (TestSpec.setup) is private and superMethod (BaseSpec.setup) is public.

    private void checkMethodForWeakerAccessPrivileges(MethodNode mn, ClassNode cn) {
        if (mn.isPublic()) return;
        Parameter[] params = mn.getParameters();
        for (MethodNode superMethod : cn.getSuperClass().getMethods(mn.getName())) {
            Parameter[] superParams = superMethod.getParameters();
            if (!hasEqualParameterTypes(params, superParams)) continue;
            if ((mn.isPrivate() && !superMethod.isPrivate()) ||
                    (mn.isProtected() && superMethod.isPublic())) {
                addWeakerAccessError(cn, mn, params, superMethod);
                return;
            }
        }
    }

eric-milles avatar Nov 08 '17 22:11 eric-milles

SpecRewriter.visitMethod(Method) line 238 is responsible for altering the modifier from public to private. This only occurs for TestSpec since it is the active compilation unit. Reconcile does not apply transforms to supertypes.

eric-milles avatar Nov 08 '17 22:11 eric-milles

If you mark setup() private in the base class, the error subsides. This may be a possible workaround; I'm not sure if it impacts the execution of the tests.

eric-milles avatar Nov 08 '17 22:11 eric-milles

Thanks for digging into this again, @eric-milles. I cannot test it at the moment, being on the road with only a tablet computer. Are you suggesting a ticket should be created for Spock in order to resolve this, or is is rather a Groovy-Eclipse issue?

kriegaex avatar Nov 09 '17 10:11 kriegaex

Definitely a groovy-elcipse issue. Active compilation unit has transforms applied, but parent source units do not. This causes this issue, and one I ran into with trait extending trait, and likely others.

eric-milles avatar Nov 09 '17 14:11 eric-milles

Ping

kriegaex avatar Apr 30 '18 12:04 kriegaex

I haven't forgotten this. It has been assigned to the 3.0.0 milestone. And I have run some experiments for possible solutions. I don't yet have a complete solution. If you'd like to try what I have, you can follow the early-xforms branch. I may need to push some stuff to it that I have locally if you are really interested.

eric-milles avatar Apr 30 '18 13:04 eric-milles

I don't wanna push you too hard. That it is WIP and not forgotten is good enough for me.

kriegaex avatar Apr 30 '18 14:04 kriegaex

I got this same error and we have a setup that looks like this:

class testSpec extends TestBase {
    void setup() {
    }
}

class TestBase extends Specification {
    @Delegate PipelineTestBase pipelineTestBase = new PipelineTestBase()
}

import com.lesfurets.jenkins.unit.BasePipelineTest
class PipelineTestBase extends BasePipelineTest {

    void setup() {
        this.setUp()
    }

    @Override
    @Before
    void setUp() {
    }
}

When I moved the setup method from PipelineTestBase to TestBase, the error went away. Also, in my case, the error was consistent and was occurring even with a fresh Gradle daemon. There is no IDE involved.

haridsv avatar Nov 25 '19 17:11 haridsv