Skip to content

Problems with static inner classes and static imports #1292

@kriegaex

Description

@kriegaex

Situation

I have a Spock 1.3, Groovy 2.5 playground project in which for many years I have used Groovy-Eclipse as a Maven Compiler back-end. the project contains many example classes and Spock tests, mostly results of me supporting other users on StackOverflow concerning Spock and Geb. The compiler source and target levels are Java 8, the JDK running the Maven builds was also 8.

Lately, I wanted to upgrade some dependencies to more recent versions and also see if I could run the build on Java 16, but still with source/target 8. What I upgraded was:

  • Groovy 2.5.8 → 2.5.14
  • Groovy Eclipse Batch 2.5.12-02 → 2.5.14-02
  • Groovy Eclipse Compiler 3.6.0-03 → 3.7.0
  • Lombok 1.18.16 →1.18.20 for JDK 16 compatibility (just in case it is relevant, but my problems are in classes not using Lombok)

Problem 1: cannot use static import of Java enum constant

I have this Java enum:

package de.scrum_master.stackoverflow;

import org.togglz.core.Feature;
import org.togglz.core.annotation.EnabledByDefault;
import org.togglz.core.context.FeatureContext;
import org.togglz.core.manager.FeatureManager;
import org.togglz.core.metadata.FeatureMetaData;
import org.togglz.core.repository.FeatureState;

public enum PocToggle implements Feature {
  @EnabledByDefault
  USE_MY_FEATURE;

  private FeatureManager customFeatureManager;

  void setFeatureManager(FeatureManager featureManager) {
    this.customFeatureManager = featureManager;
  }

  public boolean isActive() {
    FeatureManager featureManager = customFeatureManager != null
      ? customFeatureManager
      : FeatureContext.getFeatureManager();

    try {
      return featureManager.isActive(this);
    } catch (RuntimeException ignored) {
      System.err.println(String.format("Failed to retrieve feature '%s' state", this.name()));
      FeatureMetaData metaData = featureManager.getMetaData(this);
      FeatureState featureState = metaData.getDefaultFeatureState();
      return featureState.isEnabled();
    }
  }
}

In a Spock test (same package, but in src/test/groovy, not in src/main/java), I use this import:

import static PocToggle.USE_MY_FEATURE

This yields the compile error:

Compilation failure
Failure executing groovy-eclipse compiler:
----------
1. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples-powermock\src\test\groovy\de\scrum_master\stackoverflow\PocToggleTest.groovy (at line 15)
	import static PocToggle.USE_MY_FEATURE
	              ^^^^^^^^^
Groovy:unable to resolve class PocToggle
----------
1 problem (1 error)

If I replace the static import by direct enum value references, compilation passes. Before I upgraded Groovy and Groovy-Eclipse, this was not a problem-

Problem 2: cannot use static inner class

package de.scrum_master.testing

import geb.spock.GebReportingSpec
import org.openqa.selenium.WebElement
import org.openqa.selenium.interactions.*
import org.openqa.selenium.interactions.internal.Locatable
import spock.lang.Requires

import java.time.Duration

import static org.openqa.selenium.interactions.PointerInput.MouseButton.LEFT
import static spock.util.matcher.HamcrestMatchers.closeTo

class VisJsExampleIT extends GebReportingSpec {

  static class DragDropWorkaround {
    Actions dragAndDropBy(WebElement source, int xOffset, int yOffset) {
      if (isBuildingActions()) {
        action.addAction(new ClickAndHoldAction(jsonMouse, (Locatable) source))
        // The next line is a workaround for Chrome, Opera, Edge
        // but currently does not work for Chrome 75 anymore
        action.addAction(new MoveToOffsetAction(jsonMouse, null, -100, -100))
        action.addAction(new MoveToOffsetAction(jsonMouse, null, xOffset, yOffset))
        action.addAction(new ButtonReleaseAction(jsonMouse, null))
      }

      return moveInTicks(source, 0, 0)
        .tick(defaultMouse.createPointerDown(LEFT.asArg()))
        .tick(defaultMouse.createPointerMove(Duration.ofMillis(250), PointerInput.Origin.pointer(), xOffset, yOffset))
        .tick(defaultMouse.createPointerUp(LEFT.asArg()))
    }
  }

  def setupSpec() {
    Actions.mixin DragDropWorkaround
  }

}

The compiler complains:

Compilation failure
Failure executing groovy-eclipse compiler:
----------
1. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 19)
	action.addAction(new ClickAndHoldAction(jsonMouse, (Locatable) source))
	^^^^^^
Groovy:Apparent variable 'action' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
2. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 19)
	action.addAction(new ClickAndHoldAction(jsonMouse, (Locatable) source))
	                                        ^^^^^^^^^
Groovy:Apparent variable 'jsonMouse' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
3. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 22)
	action.addAction(new MoveToOffsetAction(jsonMouse, null, -100, -100))
	^^^^^^
Groovy:Apparent variable 'action' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
4. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 22)
	action.addAction(new MoveToOffsetAction(jsonMouse, null, -100, -100))
	                                        ^^^^^^^^^
Groovy:Apparent variable 'jsonMouse' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
5. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 23)
	action.addAction(new MoveToOffsetAction(jsonMouse, null, xOffset, yOffset))
	^^^^^^
Groovy:Apparent variable 'action' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
6. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 23)
	action.addAction(new MoveToOffsetAction(jsonMouse, null, xOffset, yOffset))
	                                        ^^^^^^^^^
Groovy:Apparent variable 'jsonMouse' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
7. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 24)
	action.addAction(new ButtonReleaseAction(jsonMouse, null))
	^^^^^^
Groovy:Apparent variable 'action' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
8. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 24)
	action.addAction(new ButtonReleaseAction(jsonMouse, null))
	                                         ^^^^^^^^^
Groovy:Apparent variable 'jsonMouse' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
9. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 28)
	.tick(defaultMouse.createPointerDown(LEFT.asArg()))
	      ^^^^^^^^^^^^
Groovy:Apparent variable 'defaultMouse' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
10. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 29)
	.tick(defaultMouse.createPointerMove(Duration.ofMillis(250), PointerInput.Origin.pointer(), xOffset, yOffset))
	      ^^^^^^^^^^^^
Groovy:Apparent variable 'defaultMouse' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
11. ERROR in C:\Users\alexa\Documents\java-src\GebSpockSamples\geb-spock-samples\src\test\groovy\de\scrum_master\testing\VisJsExampleIT.groovy (at line 30)
	.tick(defaultMouse.createPointerUp(LEFT.asArg()))
	      ^^^^^^^^^^^^
Groovy:Apparent variable 'defaultMouse' was found in a static scope but doesn't refer to a local variable, static field or class.
----------
11 problems (11 errors)

If I move the inner static class to outside the Geb specification and place it in the same source file, but as a "sibling" of the test class rather than a "child" like this: compilation succeeds:

package de.scrum_master.testing

import geb.spock.GebReportingSpec
import org.openqa.selenium.WebElement
import org.openqa.selenium.interactions.*
import org.openqa.selenium.interactions.internal.Locatable
import spock.lang.Requires

import java.time.Duration

import static org.openqa.selenium.interactions.PointerInput.MouseButton.LEFT
import static spock.util.matcher.HamcrestMatchers.closeTo

class VisJsExampleIT extends GebReportingSpec {

  def setupSpec() {
    Actions.mixin DragDropWorkaround
  }

}

class DragDropWorkaround {
  Actions dragAndDropBy(WebElement source, int xOffset, int yOffset) {
    if (isBuildingActions()) {
      action.addAction(new ClickAndHoldAction(jsonMouse, (Locatable) source))
      // The next line is a workaround for Chrome, Opera, Edge
      // but currently does not work for Chrome 75 anymore
      action.addAction(new MoveToOffsetAction(jsonMouse, null, -100, -100))
      action.addAction(new MoveToOffsetAction(jsonMouse, null, xOffset, yOffset))
      action.addAction(new ButtonReleaseAction(jsonMouse, null))
    }

    return moveInTicks(source, 0, 0)
      .tick(defaultMouse.createPointerDown(LEFT.asArg()))
      .tick(defaultMouse.createPointerMove(Duration.ofMillis(250), PointerInput.Origin.pointer(), xOffset, yOffset))
      .tick(defaultMouse.createPointerUp(LEFT.asArg()))
  }
}

After applying those two workaround, I can build with JDK 16 with Maven.

Problem 3: problems compiling in IntelliJ IDEA

I still cannot compile when building in IntelliJ IDEA. I am not sure it is related to the other issues or out of scope (see IDEA-275889), but I want to mention it: I guess the root cause is that IDEA somehow does not pass through source/target correctly, producing Java 16 class files (major 60) which the ASM version contained in the Groovy compiler does not recognise.

Groovyc: While compiling [tests of geb-spock-samples]: Groovyc stub generation failed
Groovyc: While compiling [tests of geb-spock-samples]: java.lang.IllegalArgumentException: Unsupported class file major version 60
	at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:196)
	at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:177)
	at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:163)
	at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:284)
	at org.codehaus.groovy.ast.decompiled.AsmDecompiler.parseClass(AsmDecompiler.java:81)
	at org.codehaus.groovy.control.ClassNodeResolver.findDecompiled(ClassNodeResolver.java:251)
	at org.codehaus.groovy.control.ClassNodeResolver.tryAsLoaderClassOrScript(ClassNodeResolver.java:189)
	at org.codehaus.groovy.control.ClassNodeResolver.findClassNode(ClassNodeResolver.java:169)
	at org.codehaus.groovy.control.ClassNodeResolver.resolveName(ClassNodeResolver.java:125)
	at org.codehaus.groovy.control.ResolveVisitor.resolveToOuter(ResolveVisitor.java:853)
	at org.codehaus.groovy.control.ResolveVisitor.resolve(ResolveVisitor.java:467)
	at org.codehaus.groovy.control.ResolveVisitor.resolveFromDefaultImports(ResolveVisitor.java:629)
	at org.codehaus.groovy.control.ResolveVisitor.resolveFromDefaultImports(ResolveVisitor.java:612)
	at org.codehaus.groovy.control.ResolveVisitor.resolveFromDefaultImports(ResolveVisitor.java:586)
	at org.codehaus.groovy.control.ResolveVisitor.resolve(ResolveVisitor.java:465)
	at org.codehaus.groovy.control.ResolveVisitor.resolve(ResolveVisitor.java:428)
	at org.codehaus.groovy.control.ResolveVisitor.resolveOrFail(ResolveVisitor.java:343)
	at org.codehaus.groovy.control.ResolveVisitor.resolveOrFail(ResolveVisitor.java:424)
	at org.codehaus.groovy.control.ResolveVisitor.visitConstructorOrMethod(ResolveVisitor.java:246)
	at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitMethod(ClassCodeVisitorSupport.java:132)
	at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:1103)
	at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:54)
	at org.codehaus.groovy.control.ResolveVisitor.visitClass(ResolveVisitor.java:1465)
	at org.codehaus.groovy.control.ResolveVisitor.startResolving(ResolveVisitor.java:230)
	at org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit$1.call(JavaAwareCompilationUnit.java:74)
	at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1084)
	at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:640)
	at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:618)
	at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:595)
	at org.jetbrains.groovy.compiler.rt.GroovyCompilerWrapper.compile(GroovyCompilerWrapper.java:48)
	at org.jetbrains.groovy.compiler.rt.DependentGroovycRunner.runGroovyc(DependentGroovycRunner.java:118)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	at org.jetbrains.groovy.compiler.rt.GroovycRunner.intMain2(GroovycRunner.java:81)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	at org.jetbrains.jps.incremental.groovy.InProcessGroovyc.runGroovycInThisProcess(InProcessGroovyc.java:167)
	at org.jetbrains.jps.incremental.groovy.InProcessGroovyc.lambda$runGroovyc$0(InProcessGroovyc.java:77)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
	at java.base/java.lang.Thread.run(Thread.java:831)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions