Bug 541532 - [11] JEP 323: Local-Variable Syntax for Lambda parameters
does not work for Function and Predicate

Change-Id: Ibf16295c5d6d262949b6c74fc95a8fbb1b9be9b3
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JEP323VarLambdaParamsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JEP323VarLambdaParamsTest.java
index 5bc4f6e..351fb85 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JEP323VarLambdaParamsTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JEP323VarLambdaParamsTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 IBM Corporation and others.
+ * Copyright (c) 2018, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -316,4 +316,29 @@
 			"\'var\' is not allowed as an element type of an array\n" + 
 			"----------\n");
 }
+public void testBug541532_01() throws IOException {
+	runConformTest(new String[] {
+			"X.java",
+			"import java.util.Arrays;\n" +
+			"import java.util.List;\n" +
+			"\n" +
+			"public class X {\n" +
+			"\n" +
+			"	public static void foo(List<String> list) {\n" +
+			"		list.stream()\n" +
+			"		  .map((var s) -> s.toLowerCase())\n" +
+			"		  .forEach(System.out::println);\n" +
+			"\n" +
+			"		list.stream()\n" +
+			"		  .filter((var s) -> s.length() == 1)\n" +
+			"		  .forEach(System.out::println);\n" +
+			"	}\n" +
+			"	public static void main(String[] args) {\n" +
+			"		String[] greetings = {\"hello\", \"world\"};\n" +
+			"		X.foo(Arrays.asList(greetings));\n" +
+			"	}\n" +
+			"}\n",
+			"hello\nworld"
+	});
+}
 }
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
index 3494069..08067d8 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 IBM Corporation and others.
+ * Copyright (c) 2012, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -136,6 +136,7 @@
 	protected Expression [] resultExpressions = NO_EXPRESSIONS;
 	public InferenceContext18 inferenceContext; // when performing tentative resolve keep a back reference to the driving context
 	private Map<Integer/*sourceStart*/, LocalTypeBinding> localTypes; // support look-up of a local type from this lambda copy
+	public boolean argumentsTypeVar = false;
 
 	
 	public LambdaExpression(CompilationResult compilationResult, boolean assistNode, boolean requiresGenericSignature) {
@@ -247,7 +248,6 @@
 	public TypeBinding resolveType(BlockScope blockScope, boolean skipKosherCheck) {
 		
 		boolean argumentsTypeElided = argumentsTypeElided();
-		boolean argumentsTypeVar = argumentsTypeVar(blockScope);
 		int argumentsLength = this.arguments == null ? 0 : this.arguments.length;
 		
 		if (this.constant != Constant.NotAConstant) {
@@ -256,7 +256,7 @@
 			if (this.original == this)
 				this.ordinal = recordFunctionalType(blockScope);
 			
-			if (!argumentsTypeElided && !argumentsTypeVar) {
+			if (!argumentsTypeElided) {
 				for (int i = 0; i < argumentsLength; i++)
 					this.argumentTypes[i] = this.arguments[i].type.resolveType(blockScope, true /* check bounds*/);
 			}
@@ -289,7 +289,7 @@
 			int parametersLength = this.descriptor.parameters.length;
 			if (parametersLength != argumentsLength) {
             	this.scope.problemReporter().lambdaSignatureMismatched(this);
-            	if (argumentsTypeElided || argumentsTypeVar || this.original != this) // no interest in continuing to error check copy.
+            	if (argumentsTypeElided || this.original != this) // no interest in continuing to error check copy.
             		return this.resolvedType = null; // FUBAR, bail out ...
             	else {
             		this.resolvedType = null; // continue to type check.
@@ -314,7 +314,7 @@
 			
 			TypeBinding argumentType;
 			final TypeBinding expectedParameterType = haveDescriptor && i < this.descriptor.parameters.length ? this.descriptor.parameters[i] : null;
-			argumentType = (argumentsTypeElided || argumentsTypeVar) ? expectedParameterType : this.argumentTypes[i];
+			argumentType = argumentsTypeElided ? expectedParameterType : this.argumentTypes[i];
 			if (argumentType == null) {
 				argumentsHaveErrors = true;
 			} else if (argumentType == TypeBinding.VOID) {
@@ -329,7 +329,7 @@
 				}
 			}
 		}
-		if (!argumentsTypeElided && !argumentsTypeVar && !argumentsHaveErrors) {
+		if (!argumentsTypeElided && !argumentsHaveErrors) {
 			ReferenceBinding groundType = null;
 			ReferenceBinding expectedSAMType = null;
 			if (this.expectedType instanceof IntersectionTypeBinding18)
@@ -365,7 +365,7 @@
 			Argument argument = this.arguments[i];
 			TypeBinding argumentType;
 			final TypeBinding expectedParameterType = haveDescriptor && i < this.descriptor.parameters.length ? this.descriptor.parameters[i] : null;
-			argumentType = (argumentsTypeElided || argumentsTypeVar) ? expectedParameterType : this.argumentTypes[i];
+			argumentType = argumentsTypeElided ? expectedParameterType : this.argumentTypes[i];
 			expectedParameterTypes[i] = expectedParameterType;
 			if (argumentType != null && argumentType != TypeBinding.VOID) {
 				if (haveDescriptor && expectedParameterType != null && argumentType.isValidBinding() && TypeBinding.notEquals(argumentType, expectedParameterType)) {
@@ -396,7 +396,7 @@
 				}
 			}
 		}
-		if (argumentsTypeVar) {
+		if (this.argumentsTypeVar) {
 			for (int i = 0; i < argumentsLength; ++i) {
 				this.arguments[i].type.resolvedType = expectedParameterTypes[i];
 			}
@@ -408,7 +408,7 @@
 				this.binding.setParameterAnnotations(parameterAnnotations);
 		}
 	
-		if (!argumentsTypeElided && !argumentsTypeVar && !argumentsHaveErrors && this.binding.isVarargs()) {
+		if (!argumentsTypeElided && !argumentsHaveErrors && this.binding.isVarargs()) {
 			if (!this.binding.parameters[this.binding.parameters.length - 1].isReifiable()) {
 				this.scope.problemReporter().possibleHeapPollutionFromVararg(this.arguments[this.arguments.length - 1]);
 			}
@@ -438,7 +438,7 @@
 		} // TODO (stephan): else? (can that happen?)
 
 		if (haveDescriptor && !argumentsHaveErrors && blockScope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) {
-			if (!argumentsTypeElided && !argumentsTypeVar) {
+			if (!argumentsTypeElided) {
 				AbstractMethodDeclaration.createArgumentBindings(this.arguments, this.binding, this.scope); // includes validation
 				// no application of null-ness default, hence also no warning regarding redundant null annotation
 				mergeParameterNullAnnotations(blockScope);
@@ -533,28 +533,7 @@
 
 	@Override
 	public boolean argumentsTypeElided() {
-		return this.arguments.length > 0 && this.arguments[0].hasElidedType();
-	}
-
-	private boolean argumentsTypeVar(BlockScope blockScope) {
-		if (blockScope.compilerOptions().complianceLevel < ClassFileConstants.getComplianceLevelForJavaVersion(ClassFileConstants.MAJOR_VERSION_11)) return false;
-		boolean retval = false, isVar = false, mixReported = false;
-		Argument[] args =  this.arguments;
-		for (int i = 0, l = args.length; i < l; ++i) {
-			Argument arg = args[i];
-			TypeReference type = arg.type;
-			if (type == null) continue;
-			boolean prev = isVar;
-			retval |= isVar = type.isTypeNameVar(blockScope);
-			if (i > 0 && prev != isVar && !mixReported) { // report only once per list
-				blockScope.problemReporter().varCannotBeMixedWithNonVarParams(isVar ? arg : args[i - 1]);
-				mixReported = true;
-			}
-			if (isVar && (type.dimensions() > 0 || type.extraDimensions() > 0)) {
-				blockScope.problemReporter().varLocalCannotBeArray(arg);
-			}
-		}
-		return retval;
+		return (this.arguments.length > 0 && this.arguments[0].hasElidedType()) || this.argumentsTypeVar;
 	}
 
 	private void analyzeExceptions() {
@@ -710,7 +689,6 @@
 
 		if (targetType == null) // assumed to signal another primary error
 			return true;
-		
 		if (argumentsTypeElided())
 			return false;
 		
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
index 885fc4c..a9e2139 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -941,6 +941,7 @@
 private int stateStackLengthStack[] = new int[0];
 protected boolean parsingJava8Plus;
 protected boolean parsingJava9Plus;
+protected boolean parsingJava11Plus;
 protected int unstackedAct = ERROR_ACTION;
 private boolean haltOnSyntaxError = false;
 private boolean tolerateDefaultClassMethods = false;
@@ -959,6 +960,7 @@
 	initializeScanner();
 	this.parsingJava8Plus = this.options.sourceLevel >= ClassFileConstants.JDK1_8;
 	this.parsingJava9Plus = this.options.sourceLevel >= ClassFileConstants.JDK9;
+	this.parsingJava11Plus = this.options.sourceLevel >= ClassFileConstants.JDK11;
 	this.astLengthStack = new int[50];
 	this.expressionLengthStack = new int[30];
 	this.typeAnnotationLengthStack = new int[30];
@@ -8552,6 +8554,31 @@
 		this.currentElement.lambdaNestLevel++;
 	}
 }
+private void setArgumentsTypeVar(LambdaExpression lexp) {
+	Argument[] args =  lexp.arguments;
+	if (!this.parsingJava11Plus || args == null || args.length == 0) {
+		lexp.argumentsTypeVar = false;
+		return;
+	}
+
+	boolean isVar = false, mixReported = false;
+	for (int i = 0, l = args.length; i < l; ++i) {
+		Argument arg = args[i];
+		TypeReference type = arg.type;
+		char[][] typeName = type != null ? type.getTypeName() : null;
+		boolean prev = isVar;
+		isVar = typeName != null && typeName.length == 1 &&
+				CharOperation.equals(typeName[0], TypeConstants.VAR);
+		lexp.argumentsTypeVar |= isVar;
+		if (i > 0 && prev != isVar && !mixReported) { // report only once per list
+			this.problemReporter().varCannotBeMixedWithNonVarParams(isVar ? arg : args[i - 1]);
+			mixReported = true;
+		}
+		if (isVar && (type.dimensions() > 0 || type.extraDimensions() > 0)) {
+			this.problemReporter().varLocalCannotBeArray(arg);
+		}
+	}
+}
 protected void consumeLambdaExpression() {
 	
 	// LambdaExpression ::= LambdaHeader LambdaBody
@@ -8581,6 +8608,7 @@
 	if (!this.parsingJava8Plus) {
 		problemReporter().lambdaExpressionsNotBelow18(lexp);
 	}
+	setArgumentsTypeVar(lexp);
 	pushOnExpressionStack(lexp);
 	if (this.currentElement != null) {
 		this.lastCheckPoint = body.sourceEnd + 1;