Bug 541772 - In Eclipse 2018-09, Maven "runtime" scoped dependencies
cause compilation errors even when not used

Change-Id: I6689dbe42f75bc8285449b44e7e67db049ecca2f
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
index de0df41..6a2b643 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
@@ -52421,4 +52421,81 @@
 			});
 	}
 }
+public void testBug541772() {
+	runConformTest(
+		new String[] {
+			"bug541772Runtime/GeneratedMessage.java",
+			"package bug541772Runtime;\n" +
+			"\n" +
+			"public class GeneratedMessage {\n" +
+			"    public class Builder<T> {\n" +
+			"    }\n" +
+			"}\n" +
+			"",
+		},
+		"",
+		getCompilerOptions()
+	);
+	
+	runConformTest(
+	new String[] {
+		"token/Token.java",
+		"package token;\n" +
+		"\n" +
+		"public class Token {\n" +
+		"  \n" +
+		"  public Token() {\n" +
+		"  }\n" +
+		"\n" +
+		"  public Token(TokenProto tokenPB) {\n" +
+		"      tokenPB.hashCode();\n" +
+		"  }\n" +
+		"  public Token(String x) {\n" +
+		"      x.hashCode();\n" +
+		"  }\n" +
+		"}\n" +
+		"",
+		"token/TokenProto.java",
+		"package token;\n" +
+		"\n" +
+		"import bug541772Runtime.GeneratedMessage;\n" +
+		"\n" +
+		"public class TokenProto {\n" +
+		"\n" +
+		"    public TokenProto(GeneratedMessage.Builder<?> builder) {\n" +
+		"        builder.hashCode();\n" +
+		"    }\n" +
+		"}\n" +
+		"",
+	},
+	"",
+	null /*classLibraries*/,
+	false /*shouldFlushOutputDirectory*/,
+	null /*vmArguments*/,
+	getCompilerOptions(),
+	null /*customRequestor*/);
+
+	Util.flushDirectoryContent(new File(OUTPUT_DIR + File.separator + "bug541772Runtime"));
+
+	runConformTest(
+	new String[] {
+		"pkg/Example.java",
+		"package pkg;\n" +
+		"\n" +
+		"import token.Token;\n" +
+		"\n" +
+		"public abstract class Example {\n" +
+		"	public static void setConnectorInfo() {\n" +
+		"		new Token(\"\");\n" +
+		"	}\n" +
+		"}\n" +
+		"",
+	},
+	"",
+	null /*classLibraries*/,
+	false /*shouldFlushOutputDirectory*/,
+	null /*vmArguments*/,
+	getCompilerOptions(),
+	null /*customRequestor*/);
+}
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java
index 14abe12..5678f8d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java
@@ -1866,6 +1866,9 @@
 }
 
 private TypeBinding annotateType(TypeBinding binding, ITypeAnnotationWalker walker, char[][][] missingTypeNames) {
+	if (walker == ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER) {
+		return binding;
+	}
 	int depth = binding.depth() + 1;
 	if (depth > 1) {
 		// need to count non-static nesting levels, resolved binding required for precision
@@ -1974,13 +1977,13 @@
 
 	// type must be a ReferenceBinding at this point, cannot be a BaseTypeBinding or ArrayTypeBinding
 	ReferenceBinding actualType = (ReferenceBinding) type;
-	if (actualType instanceof UnresolvedReferenceBinding)
+	if (walker != ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER && actualType instanceof UnresolvedReferenceBinding)
 		if (actualType.depth() > 0)
 			actualType = (ReferenceBinding) BinaryTypeBinding.resolveType(actualType, this, false /* no raw conversion */); // must resolve member types before asking for enclosingType
 	ReferenceBinding actualEnclosing = actualType.enclosingType();
 
 	ITypeAnnotationWalker savedWalker = walker;
-	if(actualType.depth() > 0) {
+	if(walker != ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER && actualType.depth() > 0) {
 		int nonStaticNestingLevels = countNonStaticNestingLevels(actualType);
 		for (int i = 0; i < nonStaticNestingLevels; i++) {
 			walker = walker.toNextNestedType();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
index 438ab5a..6de4c8e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
@@ -80,7 +80,7 @@
 
 	public ParameterizedTypeBinding(ReferenceBinding type, TypeBinding[] arguments,  ReferenceBinding enclosingType, LookupEnvironment environment){
 		this.environment = environment;
-		this.enclosingType = enclosingType; // never unresolved, never lazy per construction
+		this.enclosingType = enclosingType; // never unresolved, but if type is an unresolved nested type, enclosingType is null here but set later in swapUnresolved.
 		if (!type.hasEnclosingInstanceContext() && arguments == null && !(this instanceof RawTypeBinding))
 			throw new IllegalStateException();
 		initialize(type, arguments);
@@ -447,6 +447,9 @@
 	 */
 	@Override
 	public ReferenceBinding enclosingType() {
+		if (this.type instanceof UnresolvedReferenceBinding && ((UnresolvedReferenceBinding) this.type).depth() > 0) {
+			((UnresolvedReferenceBinding) this.type).resolve(this.environment, false); // may set enclosingType as side effect
+		}
 	    return this.enclosingType;
 	}
 
@@ -1458,7 +1461,7 @@
 			update = true;
 			ReferenceBinding enclosing = resolvedType.enclosingType();
 			if (enclosing != null) {
-				this.enclosingType = (ReferenceBinding) env.convertUnresolvedBinaryToRawType(enclosing); // needed when binding unresolved member type
+				this.enclosingType = resolvedType.isStatic() ? enclosing : (ReferenceBinding) env.convertUnresolvedBinaryToRawType(enclosing); // needed when binding unresolved member type
 			}
 		}
 		if (this.arguments != null) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/RawTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/RawTypeBinding.java
index 6f2febb..5888abf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/RawTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/RawTypeBinding.java
@@ -327,4 +327,11 @@
 	public ReferenceBinding downwardsProjection(Scope scope, TypeBinding[] mentionedTypeVariables) {
 		return this;
 	}
+	
+	@Override
+	public ReferenceBinding enclosingType() {
+		// ParameterizedTypeBinding earlier always had a resolved enclosed type, but now it does on-demand resolving.
+		// Behaviour for RawTypeBinding should be unchanged. 
+	    return this.enclosingType;
+	}
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java
index 58eb392..615d87d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java
@@ -97,7 +97,7 @@
 					this.type = resolvedType; // cannot be raw since being parameterized below
 					ReferenceBinding enclosing = resolvedType.enclosingType();
 					if (enclosing != null) {
-						this.enclosingType = (ReferenceBinding) env.convertUnresolvedBinaryToRawType(enclosing); // needed when binding unresolved member type
+						this.enclosingType = resolvedType.isStatic() ? enclosing : (ReferenceBinding) env.convertUnresolvedBinaryToRawType(enclosing); // needed when binding unresolved member type
 					}
 				}
 				if (this.arguments != null) {
@@ -122,7 +122,7 @@
 			@Override
 			public int hashCode() {
 				final int prime=31;
-				int hashCode = 1 + hash(this.type) + (this.enclosingType != null ? hash(this.enclosingType) : 0);
+				int hashCode = 1 + hash(this.type);
 				for (int i = 0, length = this.arguments == null ? 0 : this.arguments.length; i < length; i++) {
 					hashCode = hashCode * prime + hash(this.arguments[i]);
 				}
@@ -156,7 +156,7 @@
 				if (parameterizedType.actualType() != genericTypeToMatch) { //$IDENTITY-COMPARISON$
 					continue;
 				}
-				if (parameterizedType.enclosingType() != enclosingTypeToMatch //$IDENTITY-COMPARISON$
+				if (parameterizedType.enclosingType != enclosingTypeToMatch //$IDENTITY-COMPARISON$
 						|| !Util.effectivelyEqual(parameterizedType.typeArguments(), typeArgumentsToMatch)) 
 					continue;
 				if (Util.effectivelyEqual(annotations, parameterizedType.getTypeAnnotations()))
@@ -311,6 +311,11 @@
 	*/ 
 	public ParameterizedTypeBinding getParameterizedType(ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType) {
 		ReferenceBinding unannotatedGenericType = (ReferenceBinding) getUnannotatedType(genericType);
+		// getUnannotatedType may have replaced URB by resolvedType
+		if (enclosingType == null && genericType instanceof UnresolvedReferenceBinding
+				&& !(unannotatedGenericType instanceof UnresolvedReferenceBinding)) {
+			enclosingType = unannotatedGenericType.enclosingType();
+		}
 		int typeArgumentsLength = typeArguments == null ? 0: typeArguments.length;
 		TypeBinding [] unannotatedTypeArguments = typeArguments == null ? null : new TypeBinding[typeArgumentsLength];
 		for (int i = 0; i < typeArgumentsLength; i++) {