Sonar-java: ArrayIndexOutOfBoundsException for nested lambdas

The sonarqube gradle plugin fails for certain types of nested lambdas. If you have a Function that returns a Supplier and you define both as lambdas, then sonarqube fails with an exception.

Versions used

  • SonarQube: Community Edition - Version 7.8 (build 26217)
  • SonarJava plugin: 5.13 (build 18197)
  • JaCoCo plugin: 1.0.1 (build 143)
  • sonarqube gradle plugin: 2.7.1
  • Java: OpenJDK 1.8.0_222

Error observed

The following class causes an ArrayIndexOutOfBoundsException (stack trace: see comment) in the sonarqube gradle task:

import java.util.function.Function;
import java.util.function.Supplier;

class SonarNestedLambdas {
    static void fun() {
        Function<Integer, Supplier<Integer>> causesException =
                someParameter -> {
                    // Imagine more code here that prevents replacement of outer lambda
                    // with expression lambda like someParameter -> () -> 42.
                    return () -> 42;
                };
    }
}

Steps to reproduce

  1. Add the above class to your project.
  2. Run ./gradlew sonarqube.

Potential workaround

Introduce a local variable for the inner lambda:

        Function<Integer, Supplier<Integer>> noExceptionWithLocalVariableForInnerLambda =
                someParameter -> {
                    Supplier<Integer> supplier = () -> 42;
                    return supplier;
                };

This is the error message:

Unable to create symbol table for : 'src/test/java/SonarNestedLambdas.java'

This is the stack trace:

java.lang.ArrayIndexOutOfBoundsException: 0
	at com.google.common.collect.RegularImmutableList.get(RegularImmutableList.java:60)
	at org.sonar.java.resolve.TypeAndReferenceSolver.visitLambdaExpression(TypeAndReferenceSolver.java:559)
	at org.sonar.java.model.expression.LambdaExpressionTreeImpl.accept(LambdaExpressionTreeImpl.java:91)
	at org.sonar.java.resolve.TypeAndReferenceSolver.setInferedType(TypeAndReferenceSolver.java:320)
	at org.sonar.java.resolve.TypeAndReferenceSolver.visitReturnStatement(TypeAndReferenceSolver.java:647)
	at org.sonar.java.model.statement.ReturnStatementTreeImpl.accept(ReturnStatementTreeImpl.java:69)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:40)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:34)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.visitBlock(BaseTreeVisitor.java:85)
	at org.sonar.java.model.statement.BlockTreeImpl.accept(BlockTreeImpl.java:77)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:40)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.visitLambdaExpression(BaseTreeVisitor.java:355)
	at org.sonar.java.resolve.TypeAndReferenceSolver.visitLambdaExpression(TypeAndReferenceSolver.java:570)
	at org.sonar.java.model.expression.LambdaExpressionTreeImpl.accept(LambdaExpressionTreeImpl.java:91)
	at org.sonar.java.resolve.TypeAndReferenceSolver.setInferedType(TypeAndReferenceSolver.java:320)
	at org.sonar.java.resolve.TypeAndReferenceSolver.visitVariable(TypeAndReferenceSolver.java:930)
	at org.sonar.java.model.declaration.VariableTreeImpl.accept(VariableTreeImpl.java:186)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:40)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:34)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.visitBlock(BaseTreeVisitor.java:85)
	at org.sonar.java.model.statement.BlockTreeImpl.accept(BlockTreeImpl.java:77)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:40)
	at org.sonar.java.resolve.TypeAndReferenceSolver.visitMethod(TypeAndReferenceSolver.java:137)
	at org.sonar.java.model.declaration.MethodTreeImpl.accept(MethodTreeImpl.java:219)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:40)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:34)
	at org.sonar.java.resolve.TypeAndReferenceSolver.visitClass(TypeAndReferenceSolver.java:146)
	at org.sonar.java.model.declaration.ClassTreeImpl.accept(ClassTreeImpl.java:203)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:40)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:34)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.visitCompilationUnit(BaseTreeVisitor.java:52)
	at org.sonar.java.resolve.SemanticModel.createFor(SemanticModel.java:65)
	at org.sonar.java.model.VisitorsBridge.visitFile(VisitorsBridge.java:122)
	at org.sonar.java.ast.JavaAstScanner.simpleScan(JavaAstScanner.java:90)
	at org.sonar.java.ast.JavaAstScanner.scan(JavaAstScanner.java:67)
	at org.sonar.java.JavaSquid.scanTests(JavaSquid.java:121)
	at org.sonar.java.JavaSquid.scan(JavaSquid.java:110)
	at org.sonar.plugins.java.JavaSquidSensor.execute(JavaSquidSensor.java:88)
	at org.sonar.scanner.sensor.AbstractSensorWrapper.analyse(AbstractSensorWrapper.java:48)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:85)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.lambda$execute$1(ModuleSensorsExecutor.java:59)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.withModuleStrategy(ModuleSensorsExecutor.java:77)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:59)
	at org.sonar.scanner.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:82)
	at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
	at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
	at org.sonar.scanner.scan.ProjectScanContainer.scan(ProjectScanContainer.java:400)
	at org.sonar.scanner.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:395)
	at org.sonar.scanner.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:358)
	at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
	at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
	at org.sonar.scanner.bootstrap.GlobalContainer.doAfterStart(GlobalContainer.java:141)
	at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
	at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
	at org.sonar.batch.bootstrapper.Batch.doExecute(Batch.java:73)
	at org.sonar.batch.bootstrapper.Batch.execute(Batch.java:67)
	at org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:46)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.sonarsource.scanner.api.internal.IsolatedLauncherProxy.invoke(IsolatedLauncherProxy.java:60)
	at com.sun.proxy.$Proxy60.execute(Unknown Source)
	at org.sonarsource.scanner.api.EmbeddedScanner.doExecute(EmbeddedScanner.java:185)
	at org.sonarsource.scanner.api.EmbeddedScanner.execute(EmbeddedScanner.java:137)
	at org.sonarqube.gradle.SonarQubeTask.run(SonarQubeTask.java:100)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:46)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:39)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:26)
	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:801)
	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:768)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:131)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:301)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:293)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:120)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:99)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:59)
	at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
	at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59)
	at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101)
	at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44)
	at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91)
	at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
	at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.run(EventFiringTaskExecuter.java:51)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:301)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:293)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:46)
	at org.gradle.execution.taskgraph.LocalTaskInfoExecutor.execute(LocalTaskInfoExecutor.java:42)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:277)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:262)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:135)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:130)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.execute(DefaultTaskPlanExecutor.java:200)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.executeWithWork(DefaultTaskPlanExecutor.java:191)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.run(DefaultTaskPlanExecutor.java:130)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
	at java.lang.Thread.run(Thread.java:748)

The exception does not occur for the following constructs:

  • Local variable for inner lambda (see Possible workarounds above).
  • Outer lambda is an expression lambda, too:
Function<Integer, Supplier<Integer>> noExceptionWithLambdaChain =
        someParameter -> () -> 42;
  • Supplier in Supplier:
Supplier<Supplier<Integer>> noExceptionForSupplierInSupplier =
       () -> {
           return () -> 42;
       };

Hello @tobiasgradl,

thank you for reporting this issue. The problem is in a way we compute the type for returned lambda. I created the ticket https://jira.sonarsource.com/browse/SONARJAVA-3121 . We have a plan to rework the whole semantic engine in the java plugin in the following months, this should be fixed with the rework.

Thank you for creating the ticket, @saberduck! I’m glad to hear that the issue is going to be fixed with the semantic engine rework. There is no need to rush it, because the workaround is simple.