Visibility errors when recompiling: "cannot override ... attempting to assign weaker access privileges; was public"
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.
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.
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?
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:

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().
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.
I was able to recreate this one by adding Spock's global transform to the editor: -Dgreclipse.globalTransformsInReconcile=org.spockframework.compiler.SpockTransform

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;
}
}
}
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.
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.
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?
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.
Ping
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.
I don't wanna push you too hard. That it is WIP and not forgotten is good enough for me.
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.