Bug 514203 - [quick assist] Lambda: improve handling of wildcards &
captures and avoid creating redundant @NonNull

Change-Id: I70a371ffa5fa2180b60a9bc976d7d859422b842b
Signed-off-by: Till Brychcy <register.eclipse@brychcy.de>
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java
index dfa33cf..154d8cd 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2016 IBM Corporation and others.
+ * Copyright (c) 2013, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -18,6 +18,7 @@
 import org.eclipse.jdt.testplugin.JavaProjectHelper;
 import org.eclipse.jdt.testplugin.TestOptions;
 
+import org.eclipse.core.runtime.Path;
 import org.eclipse.jface.preference.IPreferenceStore;
 
 import org.eclipse.jdt.core.ICompilationUnit;
@@ -4555,4 +4556,625 @@
 		assertProposalDoesNotExist(proposals, CorrectionMessages.QuickAssistProcessor_removeParenthesesInLambda);
 	}
 
+	public void testBug514203_wildCard() throws Exception {
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+		StringBuffer buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.util.Comparator;\n");
+		buf.append("import java.util.List;\n");
+		buf.append("\n");
+		buf.append("public class Lambda1 {\n");
+		buf.append("	Comparator<List<?>> c = (l1, l2) -> Integer.compare(l1.size(), l2.size());\n");
+		buf.append("}\n");
+		ICompilationUnit cu= pack1.createCompilationUnit("Lambda1.java", buf.toString(), false, null);
+
+		AssistContext context= getCorrectionContext(cu, buf.toString().indexOf("->"), 0);
+		assertNoErrors(context);
+		List<IJavaCompletionProposal> proposals= collectAssists(context, false);
+
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.util.Comparator;\n");
+		buf.append("import java.util.List;\n");
+		buf.append("\n");
+		buf.append("public class Lambda1 {\n");
+		buf.append("	Comparator<List<?>> c = (List<?> l1, List<?> l2) -> Integer.compare(l1.size(), l2.size());\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Add inferred lambda parameter types", proposals);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.util.Comparator;\n");
+		buf.append("import java.util.List;\n");
+		buf.append("\n");
+		buf.append("public class Lambda1 {\n");
+		buf.append("	Comparator<List<?>> c = new Comparator<List<?>>() {\n");
+		buf.append("        @Override\n");
+		buf.append("        public int compare(List<?> l1, List<?> l2) {\n");
+		buf.append("            return Integer.compare(l1.size(), l2.size());\n");
+		buf.append("        }\n");
+		buf.append("    };\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to anonymous class creation", proposals);
+		
+	}
+
+	public void testBug514203_capture1() throws Exception {
+		Hashtable<String, String> options= JavaCore.getOptions();
+		options.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED);
+		JavaCore.setOptions(options);
+		JavaProjectHelper.addLibrary(fJProject1, new Path(Java18ProjectTestSetup.getJdtAnnotations20Path()));
+		
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+
+		StringBuffer buf;
+
+		buf= new StringBuffer();
+		buf.append("@org.eclipse.jdt.annotation.NonNullByDefault\n");
+		buf.append("package test1;\n");
+		pack1.createCompilationUnit("package-info.java", buf.toString(), false, null);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("public class Lambda2 {\n");
+		buf.append("	interface Sink<T> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<? extends Number> source) {\n");
+		buf.append("		source.sendTo(a -> a.doubleValue());\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		ICompilationUnit cu= pack1.createCompilationUnit("Lambda2.java", buf.toString(), false, null);
+
+		AssistContext context= getCorrectionContext(cu, buf.toString().indexOf("->"), 0);
+		assertNoErrors(context);
+		List<IJavaCompletionProposal> proposals= collectAssists(context, false);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("public class Lambda2 {\n");
+		buf.append("	interface Sink<T> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<? extends Number> source) {\n");
+		buf.append("		source.sendTo(Number::doubleValue);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to method reference", proposals);
+
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("public class Lambda2 {\n");
+		buf.append("	interface Sink<T> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<? extends Number> source) {\n");
+		buf.append("		source.sendTo((Number a) -> a.doubleValue());\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Add inferred lambda parameter types", proposals);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("public class Lambda2 {\n");
+		buf.append("	interface Sink<T> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<? extends Number> source) {\n");
+		buf.append("		source.sendTo(new Sink<Number>() {\n");
+		buf.append("            @Override\n");
+		buf.append("            public void receive(Number a) {\n");
+		buf.append("                a.doubleValue();\n");
+		buf.append("            }\n");
+		buf.append("        });\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to anonymous class creation", proposals);
+		
+	}
+
+	public void testBug514203_capture2() throws Exception {
+		Hashtable<String, String> options= JavaCore.getOptions();
+		options.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED);
+		JavaCore.setOptions(options);
+		JavaProjectHelper.addLibrary(fJProject1, new Path(Java18ProjectTestSetup.getJdtAnnotations20Path()));
+		
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+
+		StringBuffer buf;
+
+		buf= new StringBuffer();
+		buf.append("@org.eclipse.jdt.annotation.NonNullByDefault\n");
+		buf.append("package test1;\n");
+		pack1.createCompilationUnit("package-info.java", buf.toString(), false, null);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.math.BigDecimal;\n");
+		buf.append("\n");
+		buf.append("public class Lambda3 {\n");
+		buf.append("	interface Sink<T extends Number> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U extends BigDecimal> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<?> source) {\n");
+		buf.append("		source.sendTo(a -> a.scale());\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		ICompilationUnit cu= pack1.createCompilationUnit("Lambda3.java", buf.toString(), false, null);
+
+		AssistContext context= getCorrectionContext(cu, buf.toString().indexOf("->"), 0);
+		assertNoErrors(context);
+		List<IJavaCompletionProposal> proposals= collectAssists(context, false);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.math.BigDecimal;\n");
+		buf.append("\n");
+		buf.append("public class Lambda3 {\n");
+		buf.append("	interface Sink<T extends Number> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U extends BigDecimal> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<?> source) {\n");
+		buf.append("		source.sendTo(BigDecimal::scale);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to method reference", proposals);
+
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.math.BigDecimal;\n");
+		buf.append("\n");
+		buf.append("public class Lambda3 {\n");
+		buf.append("	interface Sink<T extends Number> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U extends BigDecimal> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<?> source) {\n");
+		buf.append("		source.sendTo((BigDecimal a) -> a.scale());\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Add inferred lambda parameter types", proposals);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.math.BigDecimal;\n");
+		buf.append("\n");
+		buf.append("public class Lambda3 {\n");
+		buf.append("	interface Sink<T extends Number> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U extends BigDecimal> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<?> source) {\n");
+		buf.append("		source.sendTo(new Sink<BigDecimal>() {\n");
+		buf.append("            @Override\n");
+		buf.append("            public void receive(BigDecimal a) {\n");
+		buf.append("                a.scale();\n");
+		buf.append("            }\n");
+		buf.append("        });\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to anonymous class creation", proposals);
+		
+	}
+
+	public void testBug514203_capture3() throws Exception {
+		Hashtable<String, String> options= JavaCore.getOptions();
+		options.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED);
+		JavaCore.setOptions(options);
+		JavaProjectHelper.addLibrary(fJProject1, new Path(Java18ProjectTestSetup.getJdtAnnotations20Path()));
+		
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+
+		StringBuffer buf;
+
+		buf= new StringBuffer();
+		buf.append("@org.eclipse.jdt.annotation.NonNullByDefault\n");
+		buf.append("package test1;\n");
+		pack1.createCompilationUnit("package-info.java", buf.toString(), false, null);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.util.ArrayList;\n");
+		buf.append("import java.util.List;\n");
+		buf.append("\n");
+		buf.append("public class Lambda4 {\n");
+		buf.append("	interface Sink<T extends List<Number>> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U extends ArrayList<Number>> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<?> source) {\n");
+		buf.append("		source.sendTo(a -> a.size());\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		ICompilationUnit cu= pack1.createCompilationUnit("Lambda4.java", buf.toString(), false, null);
+
+		AssistContext context= getCorrectionContext(cu, buf.toString().indexOf("->"), 0);
+		assertNoErrors(context);
+		List<IJavaCompletionProposal> proposals= collectAssists(context, false);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.util.ArrayList;\n");
+		buf.append("import java.util.List;\n");
+		buf.append("\n");
+		buf.append("public class Lambda4 {\n");
+		buf.append("	interface Sink<T extends List<Number>> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U extends ArrayList<Number>> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<?> source) {\n");
+		buf.append("		source.sendTo(ArrayList<Number>::size);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to method reference", proposals);
+
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.util.ArrayList;\n");
+		buf.append("import java.util.List;\n");
+		buf.append("\n");
+		buf.append("public class Lambda4 {\n");
+		buf.append("	interface Sink<T extends List<Number>> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U extends ArrayList<Number>> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<?> source) {\n");
+		buf.append("		source.sendTo((ArrayList<Number> a) -> a.size());\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Add inferred lambda parameter types", proposals);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.util.ArrayList;\n");
+		buf.append("import java.util.List;\n");
+		buf.append("\n");
+		buf.append("public class Lambda4 {\n");
+		buf.append("	interface Sink<T extends List<Number>> {\n");
+		buf.append("		void receive(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	interface Source<U extends ArrayList<Number>> {\n");
+		buf.append("		void sendTo(Sink<? super U> c);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	void f(Source<?> source) {\n");
+		buf.append("		source.sendTo(new Sink<ArrayList<Number>>() {\n");
+		buf.append("            @Override\n");
+		buf.append("            public void receive(ArrayList<Number> a) {\n");
+		buf.append("                a.size();\n");
+		buf.append("            }\n");
+		buf.append("        });\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to anonymous class creation", proposals);
+		
+	}
+
+	public void testBug514203_lambdaNN() throws Exception {
+		Hashtable<String, String> options= JavaCore.getOptions();
+		options.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED);
+		JavaCore.setOptions(options);
+		JavaProjectHelper.addLibrary(fJProject1, new Path(Java18ProjectTestSetup.getJdtAnnotations20Path()));
+		
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+
+		StringBuffer buf;
+
+		// --- Set up @NonNullByDefault for the package, including ARRAY_CONTENTS --
+
+		buf= new StringBuffer();
+		buf.append("@NonNullByDefault({ PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT, ARRAY_CONTENTS })\n");
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import static org.eclipse.jdt.annotation.DefaultLocation.*;\n");
+		buf.append("import org.eclipse.jdt.annotation.NonNullByDefault;\n");
+		pack1.createCompilationUnit("package-info.java", buf.toString(), false, null);
+
+		// --- Classes that are only referenced --
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.lang.annotation.ElementType;\n");
+		buf.append("import java.lang.annotation.Target;\n");
+		buf.append("\n");
+		buf.append("@Target(ElementType.TYPE_USE)\n");
+		buf.append("@interface X {}\n");
+		pack1.createCompilationUnit("X.java", buf.toString(), false, null);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.lang.annotation.ElementType;\n");
+		buf.append("import java.lang.annotation.Target;\n");
+		buf.append("\n");
+		buf.append("@Target(ElementType.TYPE_USE)\n");
+		buf.append("@interface Y {}\n");
+		pack1.createCompilationUnit("Y.java", buf.toString(), false, null);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import java.lang.annotation.ElementType;\n");
+		buf.append("import java.lang.annotation.Target;\n");
+		buf.append("\n");
+		buf.append("@Target(ElementType.TYPE_USE)\n");
+		buf.append("@interface Z {}\n");
+		pack1.createCompilationUnit("Z.java", buf.toString(), false, null);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("class Ref<A> {}\n");
+		pack1.createCompilationUnit("Ref.java", buf.toString(), false, null);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("interface SAM<A> {\n");
+		buf.append("	void f(A[] a);\n");
+		buf.append("}\n");
+		pack1.createCompilationUnit("SAM.java", buf.toString(), false, null);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("public class Test {\n");
+		buf.append("	static int nn(Object o) {\n");
+		buf.append("		return 0;\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		buf.append("");
+		pack1.createCompilationUnit("Test.java", buf.toString(), false, null);
+
+		// --- Classes in which the quick assists are checked (without and with NonNullByDefault in effect at the target location) --- 
+		
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import org.eclipse.jdt.annotation.*;\n");
+		buf.append("\n");
+		buf.append("public class LambdaNN1 {\n");
+		buf.append("	void g(Ref<? extends Ref<@X @Nullable String @Y [] @Z []>>[] data) {\n");
+		buf.append("		@NonNullByDefault({})\n");
+		buf.append("		SAM<? super @NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>>> sam0 = a0 -> Test.nn(a0);\n");
+		buf.append("		sam0.f(data);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		ICompilationUnit cu1= pack1.createCompilationUnit("LambdaNN1.java", buf.toString(), false, null);
+
+		AssistContext context1= getCorrectionContext(cu1, buf.toString().indexOf("->"), 0);
+		assertNoErrors(context1);
+		List<IJavaCompletionProposal> proposals1= collectAssists(context1, false);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import org.eclipse.jdt.annotation.*;\n");
+		buf.append("\n");
+		buf.append("public class LambdaNN2 {\n");
+		buf.append("	void g(Ref<? extends Ref<@X @Nullable String @Y [] @Z []>>[] data) {\n");
+		buf.append("		SAM<? super @NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>>> sam0 = a0 -> Test.nn(a0);\n");
+		buf.append("		sam0.f(data);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		ICompilationUnit cu2= pack1.createCompilationUnit("LambdaNN2.java", buf.toString(), false, null);
+
+		AssistContext context2= getCorrectionContext(cu2, buf.toString().indexOf("->"), 0);
+		assertNoErrors(context2);
+
+		List<IJavaCompletionProposal> proposals2= collectAssists(context2, false);
+
+		// --- Convert to method reference without and with NNBD ---
+		
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import org.eclipse.jdt.annotation.*;\n");
+		buf.append("\n");
+		buf.append("public class LambdaNN1 {\n");
+		buf.append("	void g(Ref<? extends Ref<@X @Nullable String @Y [] @Z []>>[] data) {\n");
+		buf.append("		@NonNullByDefault({})\n");
+		buf.append("		SAM<? super @NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>>> sam0 = Test::nn;\n");
+		buf.append("		sam0.f(data);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to method reference", proposals1);
+
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import org.eclipse.jdt.annotation.*;\n");
+		buf.append("\n");
+		buf.append("public class LambdaNN2 {\n");
+		buf.append("	void g(Ref<? extends Ref<@X @Nullable String @Y [] @Z []>>[] data) {\n");
+		buf.append("		SAM<? super @NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>>> sam0 = Test::nn;\n");
+		buf.append("		sam0.f(data);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to method reference", proposals2);
+
+		// --- Add inferred lambda parameter types without and with NNBD ---
+		
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import org.eclipse.jdt.annotation.*;\n");
+		buf.append("\n");
+		buf.append("public class LambdaNN1 {\n");
+		buf.append("	void g(Ref<? extends Ref<@X @Nullable String @Y [] @Z []>>[] data) {\n");
+		buf.append("		@NonNullByDefault({})\n");
+		buf.append("		SAM<? super @NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>>> sam0 = (@NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>> @NonNull [] a0) -> Test.nn(a0);\n");
+		buf.append("		sam0.f(data);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Add inferred lambda parameter types", proposals1);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import org.eclipse.jdt.annotation.*;\n");
+		buf.append("\n");
+		buf.append("public class LambdaNN2 {\n");
+		buf.append("	void g(Ref<? extends Ref<@X @Nullable String @Y [] @Z []>>[] data) {\n");
+		buf.append("		SAM<? super @NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>>> sam0 = (Ref<? extends @Y Ref<@X @Nullable String @Y [] @Z []>>[] a0) -> Test.nn(a0);\n");
+		buf.append("		sam0.f(data);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Add inferred lambda parameter types", proposals2);
+
+		// --- Convert to anonymous class creation without and with NNBD --
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import org.eclipse.jdt.annotation.*;\n");
+		buf.append("\n");
+		buf.append("public class LambdaNN1 {\n");
+		buf.append("	void g(Ref<? extends Ref<@X @Nullable String @Y [] @Z []>>[] data) {\n");
+		buf.append("		@NonNullByDefault({})\n");
+		buf.append("		SAM<? super @NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>>> sam0 = new SAM<@NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>>>() {\n");
+		buf.append("            @Override\n");
+		buf.append("            public void f(\n");
+		buf.append("                    @NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>> @NonNull [] a0) {\n");
+		buf.append("                Test.nn(a0);\n");
+		buf.append("            }\n");
+		buf.append("        };\n");
+		buf.append("		sam0.f(data);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to anonymous class creation", proposals1);
+		
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("import org.eclipse.jdt.annotation.*;\n");
+		buf.append("\n");
+		buf.append("public class LambdaNN2 {\n");
+		buf.append("	void g(Ref<? extends Ref<@X @Nullable String @Y [] @Z []>>[] data) {\n");
+		buf.append("		SAM<? super @NonNull Ref<? extends @NonNull @Y Ref<@X @Nullable String @Y @NonNull [] @Z @NonNull []>>> sam0 = new SAM<Ref<? extends @Y Ref<@X @Nullable String @Y [] @Z []>>>() {\n");
+		buf.append("            @Override\n");
+		buf.append("            public void f(\n");
+		buf.append("                    Ref<? extends @Y Ref<@X @Nullable String @Y [] @Z []>>[] a0) {\n");
+		buf.append("                Test.nn(a0);\n");
+		buf.append("            }\n");
+		buf.append("        };\n");
+		buf.append("		sam0.f(data);\n");
+		buf.append("	}\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to anonymous class creation", proposals2);
+	}
+	public void testBug514203_annotatedParametrizedType() throws Exception {
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+		StringBuffer buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("public class Example {\n");
+		buf.append("	@java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)\n");
+		buf.append("	public @interface X {}\n");
+		buf.append("\n");
+		buf.append("	interface SAM<T> {\n");
+		buf.append("		T f(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	@X\n");
+		buf.append("	SAM<String> c = a -> a;\n");
+		buf.append("}\n");
+		ICompilationUnit cu= pack1.createCompilationUnit("Example.java", buf.toString(), false, null);
+
+		AssistContext context= getCorrectionContext(cu, buf.toString().indexOf("->"), 0);
+		assertNoErrors(context);
+		List<IJavaCompletionProposal> proposals= collectAssists(context, false);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("\n");
+		buf.append("public class Example {\n");
+		buf.append("	@java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)\n");
+		buf.append("	public @interface X {}\n");
+		buf.append("\n");
+		buf.append("	interface SAM<T> {\n");
+		buf.append("		T f(T t);\n");
+		buf.append("	}\n");
+		buf.append("\n");
+		buf.append("	@X\n");
+		buf.append("	SAM<String> c = new @X SAM<String>() {\n");
+		buf.append("        @Override\n");
+		buf.append("        public String f(String a) {\n");
+		buf.append("            return a;\n");
+		buf.append("        }\n");
+		buf.append("    };\n");
+		buf.append("}\n");
+		assertProposalPreviewEquals(buf.toString(), "Convert to anonymous class creation", proposals);		
+	}
 }
diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/StubUtility2.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/StubUtility2.java
index 2c977a1..fe8a80f 100644
--- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/StubUtility2.java
+++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/StubUtility2.java
@@ -372,10 +372,7 @@
 		decl.setConstructor(false);
 		
 		ITypeBinding bindingReturnType= binding.getReturnType();
-		if (bindingReturnType.isWildcardType()) {
-			ITypeBinding bound= bindingReturnType.getBound();
-			bindingReturnType= (bound != null) ? bound : bindingReturnType.getErasure();
-		}
+		bindingReturnType = StubUtility2.replaceWildcardsAndCaptures(bindingReturnType);
 		
 		if (JavaModelUtil.is50OrHigher(javaProject)) {
 			createTypeParameters(imports, context, ast, binding, decl);
@@ -498,10 +495,7 @@
 		for (int i= 0; i < params.length; i++) {
 			SingleVariableDeclaration var= ast.newSingleVariableDeclaration();
 			ITypeBinding type= params[i];
-			if (type.isWildcardType()) {
-				ITypeBinding bound= type.getBound();
-				type= (bound != null) ? bound : type.getErasure();
-			}
+			type=replaceWildcardsAndCaptures(type);
 			if (!is50OrHigher) {
 				type= type.getErasure();
 				var.setType(imports.addImport(type, ast, context, TypeLocation.PARAMETER));
@@ -1008,4 +1002,12 @@
 		}
 		return null;
 	}
+
+	public static ITypeBinding replaceWildcardsAndCaptures(ITypeBinding type) {
+		while (type.isWildcardType() || type.isCapture() || (type.isArray() && type.getElementType().isCapture())) {
+			ITypeBinding bound= type.getBound();
+			type= (bound != null) ? bound : type.getErasure();
+		}
+		return type;
+	}
 }
diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java
index fba38d7..1ff93e8 100644
--- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java
+++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -17,6 +17,7 @@
 import org.eclipse.jdt.core.dom.ASTNode;
 import org.eclipse.jdt.core.dom.ASTParser;
 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnnotatableType;
 import org.eclipse.jdt.core.dom.ArrayType;
 import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.dom.Dimension;
@@ -43,7 +44,9 @@
 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
 
+import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
 import org.eclipse.jdt.internal.corext.util.JDTUIHelperClasses;
 
 /**
@@ -354,36 +357,29 @@
 		return result;
 	}
 
+	/**
+	 * Create a Type suitable as the creationType in a ClassInstanceCreation expression.
+	 * @param ast The AST to create the nodes for.
+	 * @param typeBinding binding representing the given class type
+	 * @param importRewrite the import rewrite to use
+	 * @param importContext the import context used to determine which (null) annotations to consider
+	 * @return a Type suitable as the creationType in a ClassInstanceCreation expression.
+	 */
 	public static Type newCreationType(AST ast, ITypeBinding typeBinding, ImportRewrite importRewrite, ImportRewriteContext importContext) {
 		if (typeBinding.isParameterizedType()) {
 			Type baseType= newCreationType(ast, typeBinding.getTypeDeclaration(), importRewrite, importContext);
+			IAnnotationBinding[] typeAnnotations= importContext.removeRedundantTypeAnnotations(typeBinding.getTypeAnnotations(), TypeLocation.NEW, typeBinding);
+			for (IAnnotationBinding typeAnnotation : typeAnnotations) {
+				((AnnotatableType)baseType).annotations().add(importRewrite.addAnnotation(typeAnnotation, ast, importContext));
+			}
 			ParameterizedType parameterizedType= ast.newParameterizedType(baseType);
 			for (ITypeBinding typeArgument : typeBinding.getTypeArguments()) {
-				parameterizedType.typeArguments().add(newCreationType(ast, typeArgument, importRewrite, importContext));
+				typeArgument= StubUtility2.replaceWildcardsAndCaptures(typeArgument);
+				parameterizedType.typeArguments().add(importRewrite.addImport(typeArgument, ast, importContext, TypeLocation.TYPE_ARGUMENT));
 			}
 			return parameterizedType;
-			
-		} else if (typeBinding.isParameterizedType()) {
-			Type elementType= newCreationType(ast, typeBinding.getElementType(), importRewrite, importContext);
-			ArrayType arrayType= ast.newArrayType(elementType, 0);
-			while (typeBinding.isArray()) {
-				Dimension dimension= ast.newDimension();
-				IAnnotationBinding[] typeAnnotations= typeBinding.getTypeAnnotations();
-				for (IAnnotationBinding typeAnnotation : typeAnnotations) {
-					dimension.annotations().add(importRewrite.addAnnotation(typeAnnotation, ast, importContext));
-				}
-				arrayType.dimensions().add(dimension);
-				typeBinding= typeBinding.getComponentType();
-			}
-			return arrayType;
-				
-		} else if (typeBinding.isWildcardType()) {
-			ITypeBinding bound= typeBinding.getBound();
-			typeBinding= (bound != null) ? bound : typeBinding.getErasure();
-			return newCreationType(ast, typeBinding, importRewrite, importContext);
-			
 		} else {
-			return importRewrite.addImport(typeBinding, ast, importContext);
+			return importRewrite.addImport(typeBinding, ast, importContext, TypeLocation.NEW);
 		}
 	}
 }
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java
index e61cab0..78faa07 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java
@@ -130,6 +130,7 @@
 import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
 import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
+import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
 import org.eclipse.jdt.internal.corext.dom.Bindings;
@@ -1225,7 +1226,9 @@
 				typeMethodReference.setName((SimpleName) rewrite.createCopyTarget(methodInvocation.getName()));
 				importRewrite= StubUtility.createImportRewrite(context.getASTRoot(), true);
 				ITypeBinding invocationTypeBinding= ASTNodes.getInvocationType(methodInvocation, methodBinding, invocationQualifier);
-				typeMethodReference.setType(importRewrite.addImport(invocationTypeBinding, ast));
+				invocationTypeBinding=StubUtility2.replaceWildcardsAndCaptures(invocationTypeBinding);
+				ImportRewriteContext importRewriteContext=new ContextSensitiveImportRewriteContext(lambda, importRewrite);
+				typeMethodReference.setType(importRewrite.addImport(invocationTypeBinding, ast, importRewriteContext, TypeLocation.OTHER));
 				typeMethodReference.typeArguments().addAll(getCopiedTypeArguments(rewrite, methodInvocation.typeArguments()));
 
 			} else {
@@ -1384,13 +1387,14 @@
 		ImportRewrite importRewrite= StubUtility.createImportRewrite(context.getASTRoot(), true);
 
 		rewrite.set(lambda, LambdaExpression.PARENTHESES_PROPERTY, Boolean.valueOf(true), null);
-
+		ContextSensitiveImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(lambda, importRewrite);
 		ITypeBinding[] parameterTypes= methodBinding.getParameterTypes();
 		for (int i= 0; i < noOfLambdaParams; i++) {
 			VariableDeclaration param= lambdaParameters.get(i);
 			SingleVariableDeclaration newParam= ast.newSingleVariableDeclaration();
 			newParam.setName(ast.newSimpleName(param.getName().getIdentifier()));
-			newParam.setType(importRewrite.addImport(parameterTypes[i], ast));
+			ITypeBinding type= StubUtility2.replaceWildcardsAndCaptures(parameterTypes[i]);
+			newParam.setType(importRewrite.addImport(type, ast, importRewriteContext, TypeLocation.PARAMETER));
 			rewrite.replace(param, newParam, null);
 		}