From 9177127c8c6dfa405ce89bd392014fe88412df87 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Fri, 16 Feb 2018 14:51:52 -0600 Subject: [PATCH] Fix for issue #451: enable closure proposals after method pointer --- .../jdt/groovy/search/VariableScope.java | 8 +++++- .../tests/MethodCompletionTests.groovy | 14 +++++------ ...ementAndExpressionCompletionProcessor.java | 25 +++++++++++++------ 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/VariableScope.java b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/VariableScope.java index 4d32bdd60f..96537dccb4 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/VariableScope.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/VariableScope.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -895,6 +895,12 @@ public static ClassNode clonedRange() { return clone; } + public static ClassNode clonedClosure() { + ClassNode clone = clone(CLOSURE_CLASS_NODE); + cleanGenerics(clone.getGenericsTypes()[0]); + return clone; + } + private static void cleanGenerics(GenericsType gt) { gt.getType().setGenericsTypes(null); gt.setName("java.lang.Object"); diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/MethodCompletionTests.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/MethodCompletionTests.groovy index c44af09eb5..5396d9040f 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/MethodCompletionTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/MethodCompletionTests.groovy @@ -472,19 +472,19 @@ final class MethodCompletionTests extends CompletionTestSuite { @Test void testMethodPointer1() { String contents = 'String.&isE' - ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'isE')) - proposalExists(proposals, 'isEmpty', 1) - - applyProposalAndCheck(findFirstProposal(proposals, 'isEmpty'), 'String.&isEmpty') + applyProposalAndCheck(checkUniqueProposal(contents, 'isE', 'isEmpty'), contents + 'mpty') } @Test void testMethodPointer2() { String contents = 'String.& isE' - ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'isE')) - proposalExists(proposals, 'isEmpty', 1) + applyProposalAndCheck(checkUniqueProposal(contents, 'isE', 'isEmpty'), contents + 'mpty') + } - applyProposalAndCheck(findFirstProposal(proposals, 'isEmpty'), 'String.& isEmpty') + @Test + void testMethodPointer3() { + String contents = 'String.&isEmpty.mem' + applyProposalAndCheck(checkUniqueProposal(contents, 'mem', 'memoize()'), contents + 'oize()') } @Test diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/StatementAndExpressionCompletionProcessor.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/StatementAndExpressionCompletionProcessor.java index 1a0ee8a73d..38f0328718 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/StatementAndExpressionCompletionProcessor.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/StatementAndExpressionCompletionProcessor.java @@ -35,6 +35,7 @@ import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.MethodPointerExpression; import org.codehaus.groovy.ast.expr.PropertyExpression; import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; import org.codehaus.groovy.ast.expr.TupleExpression; @@ -56,6 +57,7 @@ import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.ISourceReference; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.groovy.core.util.GroovyUtils; import org.eclipse.jdt.groovy.search.AccessorSupport; import org.eclipse.jdt.groovy.search.ITypeRequestor; import org.eclipse.jdt.groovy.search.TypeInferencingVisitorFactory; @@ -164,8 +166,10 @@ public VisitStatus acceptASTNode(ASTNode node, TypeLookupResult result, IJavaEle } private ClassNode findResultingType(TypeLookupResult result, boolean derefList) { + ContentAssistContext context = getContext(); // if completing on a method call with an implicit 'this'. - ClassNode candidate = getContext().location == ContentAssistLocation.METHOD_CONTEXT ? result.declaringType : result.type; + ClassNode candidate = (context.location == ContentAssistLocation.METHOD_CONTEXT ? result.declaringType : result.type); + if (derefList) { for (int i = 0; i < derefCount; i += 1) { // GRECLIPSE-742: does the LHS type have a 'getAt' method? @@ -177,7 +181,6 @@ private ClassNode findResultingType(TypeLookupResult result, boolean derefList) getAtFound = true; } } - if (!getAtFound) { if (VariableScope.MAP_CLASS_NODE.equals(candidate)) { // for maps, always use the type of value @@ -191,15 +194,23 @@ private ClassNode findResultingType(TypeLookupResult result, boolean derefList) } } - // now look at spread expressions - // might be part of a spread operation boolean extractElementType = false; // for spread operations + ASTNode enclosing = result.scope.getEnclosingNode(); - if (enclosing instanceof MethodCallExpression) { - extractElementType = ((MethodCallExpression) enclosing).isSpreadSafe(); - } else if (enclosing instanceof PropertyExpression) { + if (enclosing instanceof PropertyExpression) { + // if enclosing is method pointer expression, result type is Closure, not just T + if (((PropertyExpression) enclosing).getObjectExpression() instanceof MethodPointerExpression) { + ClassNode closureType = VariableScope.clonedClosure(); + Parameter[] parameters = (result.declaration instanceof MethodNode ? + ((MethodNode) result.declaration).getParameters() : Parameter.EMPTY_ARRAY); + GroovyUtils.updateClosureWithInferredTypes(closureType, candidate, parameters); + return closureType; + } extractElementType = ((PropertyExpression) enclosing).isSpreadSafe(); + } else if (enclosing instanceof MethodCallExpression) { + extractElementType = ((MethodCallExpression) enclosing).isSpreadSafe(); } + if (extractElementType) { candidate = VariableScope.extractElementType(candidate); }