You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by sr...@apache.org on 2018/09/30 15:25:36 UTC
[incubator-netbeans] branch master updated: [NETBEANS-1293] First
stab at flow typing, tests show use cases implemented
This is an automated email from the ASF dual-hosted git repository.
sreimers pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 59dd171 [NETBEANS-1293] First stab at flow typing, tests show use cases implemented
59dd171 is described below
commit 59dd171b097cd680680b0cf17321bf6a021e222a
Author: Sven Reimers <sv...@users.noreply.github.com>
AuthorDate: Sun Sep 30 17:25:31 2018 +0200
[NETBEANS-1293] First stab at flow typing, tests show use cases implemented
---
.../modules/groovy/editor/api/FindTypeUtils.java | 10 +-
.../groovy/editor/api/parser/GroovyParser.java | 22 ++--
.../completion/inference/GroovyTypeAnalyzer.java | 4 +-
.../completion/inference/MethodInference.java | 136 +++++++++++++++++++--
.../completion/inference/TypeInferenceVisitor.java | 86 ++++++++++++-
.../completion/provider/MetaElementsProvider.java | 44 ++++++-
.../groovy/editor/occurrences/TypeVisitor.java | 4 +
.../CompletionReturnType1.groovy | 33 +++++
...1.groovy.testCompletionReturnType1_1.completion | 29 +++++
...1.groovy.testCompletionReturnType1_2.completion | 29 +++++
...1.groovy.testCompletionReturnType1_3.completion | 29 +++++
...1.groovy.testCompletionReturnType1_4.completion | 29 +++++
...1.groovy.testCompletionReturnType1_5.completion | 29 +++++
...1.groovy.testCompletionReturnType1_6.completion | 29 +++++
...1.groovy.testCompletionReturnType1_7.completion | 29 +++++
.../groovy/editor/api/completion/FlowCCTest.java | 64 ++++++++++
16 files changed, 574 insertions(+), 32 deletions(-)
diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/FindTypeUtils.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/FindTypeUtils.java
index 328ba36..8ecf2ac 100644
--- a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/FindTypeUtils.java
+++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/FindTypeUtils.java
@@ -397,10 +397,12 @@ public final class FindTypeUtils {
}
private static OffsetRange getAnnotationRange(AnnotationNode annotation, BaseDocument doc, int cursorOffset) {
- final int offset = ASTUtils.getOffset(doc, annotation.getLineNumber(), annotation.getColumnNumber());
- final OffsetRange range = ASTUtils.getNextIdentifierByName(doc, annotation.getClassNode().getNameWithoutPackage(), offset);
- if (range.containsInclusive(cursorOffset)) {
- return range;
+ if (annotation.getLineNumber() != -1) {
+ final int offset = ASTUtils.getOffset(doc, annotation.getLineNumber(), annotation.getColumnNumber());
+ final OffsetRange range = ASTUtils.getNextIdentifierByName(doc, annotation.getClassNode().getNameWithoutPackage(), offset);
+ if (range.containsInclusive(cursorOffset)) {
+ return range;
+ }
}
return OffsetRange.NONE;
}
diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/parser/GroovyParser.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/parser/GroovyParser.java
index 6b4306c..f5ca9c2 100644
--- a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/parser/GroovyParser.java
+++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/parser/GroovyParser.java
@@ -33,11 +33,16 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
+import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.CompileUnit;
import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.ErrorCollector;
import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.messages.Message;
import org.codehaus.groovy.control.messages.SimpleMessage;
import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
@@ -418,12 +423,13 @@ public class GroovyParser extends Parser {
ClassPath bootPath = fo == null ? ClassPath.EMPTY : ClassPath.getClassPath(fo, ClassPath.BOOT);
ClassPath compilePath = fo == null ? ClassPath.EMPTY : ClassPath.getClassPath(fo, ClassPath.COMPILE);
ClassPath sourcePath = fo == null ? ClassPath.EMPTY : ClassPath.getClassPath(fo, ClassPath.SOURCE);
- ClassPath cp = ClassPathSupport.createProxyClassPath(bootPath, compilePath, sourcePath);
+ ClassPath transformPath = ClassPathSupport.createProxyClassPath(bootPath, compilePath);
+ ClassPath cp = ClassPathSupport.createProxyClassPath(transformPath, sourcePath);
CompilerConfiguration configuration = new CompilerConfiguration();
final ClassNodeCache classNodeCache = ClassNodeCache.get();
final GroovyClassLoader classLoader = classNodeCache.createResolveLoader(cp, configuration);
- final GroovyClassLoader transformationLoader = classNodeCache.createTransformationLoader(cp,configuration);
+ final GroovyClassLoader transformationLoader = classNodeCache.createTransformationLoader(transformPath, configuration);
ClasspathInfo cpInfo = ClasspathInfo.create(
// we should try to load everything by javac instead of classloader,
// but for now it is faster to use javac only for sources - not true
@@ -475,13 +481,15 @@ public class GroovyParser extends Parser {
int line = se.getStartLine();
- if(line < 1 )
+ if (line < 1) {
line = 1;
+ }
int col = se.getStartColumn();
- if(col < 1 )
+ if (col < 1) {
col = 1;
+ }
// display Exception information
// LOG.log(Level.FINEST, "-----------------------------------------------");
@@ -633,9 +641,9 @@ public class GroovyParser extends Parser {
displayName = description;
}
- Error error =
- new GroovyError(key, displayName, description, context.snapshot.getSource().getFileObject(),
- startOffset, endOffset, severity, CompilerErrorResolver.getId(description));
+ Error error
+ = new GroovyError(key, displayName, description, context.snapshot.getSource().getFileObject(),
+ startOffset, endOffset, severity, CompilerErrorResolver.getId(description));
context.errorHandler.error(error);
diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/GroovyTypeAnalyzer.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/GroovyTypeAnalyzer.java
index 8dc132f..0822fb5 100644
--- a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/GroovyTypeAnalyzer.java
+++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/GroovyTypeAnalyzer.java
@@ -20,10 +20,12 @@
package org.netbeans.modules.groovy.editor.completion.inference;
import java.util.Collections;
+import java.util.ListIterator;
import java.util.Set;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.netbeans.editor.BaseDocument;
@@ -55,7 +57,7 @@ public class GroovyTypeAnalyzer {
}
if (caller instanceof MethodCallExpression) {
- return Collections.singleton(MethodInference.findCallerType(caller));
+ return Collections.singleton(MethodInference.findCallerType(caller, path, document, astOffset));
}
return Collections.emptySet();
diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/MethodInference.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/MethodInference.java
index 12779e2..1ee0e8d 100644
--- a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/MethodInference.java
+++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/MethodInference.java
@@ -19,15 +19,27 @@
package org.netbeans.modules.groovy.editor.completion.inference;
+import java.util.ArrayList;
+import java.util.List;
import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.MethodNode;
-import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.tools.GenericsUtils;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.editor.BaseDocument;
+import org.netbeans.modules.groovy.editor.api.ASTUtils;
+import org.netbeans.modules.groovy.editor.api.AstPath;
/**
*
@@ -50,24 +62,38 @@ public final class MethodInference {
* @return class type of the caller if found, {@code null} otherwise
*/
@CheckForNull
- public static ClassNode findCallerType(@NonNull ASTNode expression) {
+ public static ClassNode findCallerType(@NonNull ASTNode expression, @NonNull AstPath path, BaseDocument baseDocument, int offset) {
// In case if the method call is chained with another method call
// For example: someInteger.toString().^
if (expression instanceof MethodCallExpression) {
MethodCallExpression methodCall = (MethodCallExpression) expression;
-
- ClassNode callerType = findCallerType(methodCall.getObjectExpression());
+
+ ClassNode callerType = findCallerType(methodCall.getObjectExpression(), path, baseDocument, offset);
if (callerType != null) {
- return findReturnTypeFor(callerType, methodCall.getMethodAsString(), methodCall.getArguments());
+ return findReturnTypeFor(callerType, methodCall.getMethodAsString(), methodCall.getArguments(), path, false, baseDocument, offset);
}
}
// In case if the method call is directly on a variable
if (expression instanceof VariableExpression) {
- Variable variable = ((VariableExpression) expression).getAccessedVariable();
- if (variable != null) {
- return variable.getType();
+ int newOffset = ASTUtils.getOffset(baseDocument, expression.getLineNumber(), expression.getColumnNumber());
+ AstPath newPath = new AstPath(path.root(), newOffset, baseDocument);
+ TypeInferenceVisitor tiv = new TypeInferenceVisitor(((ModuleNode)path.root()).getContext(), newPath, baseDocument, newOffset);
+ tiv.collect();
+ return tiv.getGuessedType();
+
}
+ if (expression instanceof ConstantExpression) {
+ return ((ConstantExpression) expression).getType();
+ }
+ if (expression instanceof ClassExpression) {
+ return ClassHelper.make(((ClassExpression) expression).getType().getName());
+ }
+
+ if (expression instanceof StaticMethodCallExpression) {
+ StaticMethodCallExpression staticMethodCall = (StaticMethodCallExpression) expression;
+
+ return findReturnTypeFor(staticMethodCall.getOwnerType(), staticMethodCall.getMethod(), staticMethodCall.getArguments(), path, true, baseDocument, offset);
}
return null;
}
@@ -76,12 +102,100 @@ public final class MethodInference {
private static ClassNode findReturnTypeFor(
@NonNull ClassNode callerType,
@NonNull String methodName,
- @NonNull Expression arguments) {
-
- MethodNode possibleMethod = callerType.tryFindPossibleMethod(methodName, arguments);
+ @NonNull Expression arguments,
+ @NonNull AstPath path,
+ @NonNull boolean isStatic,
+ @NonNull BaseDocument baseDocument,
+ @NonNull int offset
+ ) {
+
+ List<ClassNode> paramTypes = new ArrayList<>();
+ if (arguments instanceof ArgumentListExpression) {
+ ArgumentListExpression argExpression = (ArgumentListExpression) arguments;
+ for (Expression e : argExpression.getExpressions()) {
+ if (e instanceof VariableExpression) {
+ ModuleNode moduleNode = (ModuleNode) path.root();
+ int newOffset = ASTUtils.getOffset(baseDocument, e.getLineNumber(), e.getColumnNumber());
+ AstPath newPath = new AstPath(moduleNode, newOffset, baseDocument);
+ TypeInferenceVisitor tiv = new TypeInferenceVisitor(moduleNode.getContext(), newPath, baseDocument, newOffset);
+ tiv.collect();
+ ClassNode guessedType = tiv.getGuessedType();
+ if (null == guessedType) {
+ System.out.println("Bad guessed type");
+ } else {
+ paramTypes.add(tiv.getGuessedType());
+ }
+ } else if(e instanceof ConstantExpression) {
+ paramTypes.add(((ConstantExpression)e).getType());
+ } else if (e instanceof MethodCallExpression) {
+ paramTypes.add(findCallerType(e, path, baseDocument, offset));
+ } else if (e instanceof BinaryExpression) {
+ BinaryExpression binExpression = (BinaryExpression) e;
+ paramTypes.add(binExpression.getType());
+ } else if (e instanceof ClassExpression) {
+ ClassExpression classExpression = (ClassExpression) e;
+ // This should be Class<classExpression.getType()>
+ paramTypes.add(GenericsUtils.makeClassSafeWithGenerics(Class.class, classExpression.getType()));
+ } else {
+ System.out.println(e.getClass());
+ }
+ }
+ }
+
+ MethodNode possibleMethod = tryFindPossibleMethod(callerType, methodName, paramTypes, isStatic);
if (possibleMethod != null) {
return possibleMethod.getReturnType();
}
return null;
}
+
+ private static MethodNode tryFindPossibleMethod(ClassNode callerType, String methodName, List<ClassNode> paramTypes, boolean isStatic) {
+ int count = paramTypes.size();
+
+ MethodNode res = null;
+ ClassNode node = callerType;
+ do {
+ for (MethodNode method : node.getMethods(methodName)) {
+ if (isStatic && !method.isStatic()) {
+ continue;
+ }
+ if (method.getParameters().length == count) {
+ boolean match = true;
+ for (int i = 0; i != count; ++i) {
+ if (!paramTypes.get(i).isDerivedFrom(method.getParameters()[i].getType())) {
+ match = false;
+ break;
+ }
+ }
+
+ if (match) {
+ if (res == null) {
+ res = method;
+ } else {
+ if (res.getParameters().length != count) {
+ return null;
+ }
+ if (node.equals(callerType)) {
+ return null;
+ }
+
+ match = true;
+ for (int i = 0; i != count; ++i) {
+ if (!res.getParameters()[i].getType().equals(method.getParameters()[i].getType())) {
+ match = false;
+ break;
+ }
+ }
+ if (!match) {
+ return null;
+ }
+ }
+ }
+ }
+ }
+ node = node.getSuperClass();
+ } while (node != null);
+
+ return res;
+ }
}
diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/TypeInferenceVisitor.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/TypeInferenceVisitor.java
index 8b8836d..bc69621 100644
--- a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/TypeInferenceVisitor.java
+++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/inference/TypeInferenceVisitor.java
@@ -20,17 +20,25 @@
package org.netbeans.modules.groovy.editor.completion.inference;
import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.Variable;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.syntax.Types;
import org.netbeans.editor.BaseDocument;
+import org.netbeans.modules.groovy.editor.api.ASTUtils;
import org.netbeans.modules.groovy.editor.api.AstPath;
import org.netbeans.modules.groovy.editor.occurrences.TypeVisitor;
@@ -79,11 +87,72 @@ public class TypeInferenceVisitor extends TypeVisitor {
}
}
+ public void visitField(FieldNode node) {
+ if (sameVariableName(leaf, node)) {
+ if (node.hasInitialExpression()){
+ Expression expression = node.getInitialExpression();
+ if (expression instanceof ConstantExpression
+ && !expression.getText().equals("null")) { // NOI18N
+ guessedType = ((ConstantExpression) expression).getType();
+ } else if (expression instanceof ConstructorCallExpression) {
+ guessedType = ((ConstructorCallExpression) expression).getType();
+ } else if (expression instanceof MethodCallExpression) {
+ int newOffset = ASTUtils.getOffset(doc, expression.getLineNumber(), expression.getColumnNumber());
+ AstPath newPath = new AstPath(path.root(), newOffset, doc);
+ guessedType = MethodInference.findCallerType(expression, newPath, doc, newOffset);
+ }
+ }
+ }
+ }
+
+
@Override
- public void visitVariableExpression(VariableExpression expression) {
- if (expression == leaf) {
- leafReached = true;
+ public void visitDeclarationExpression(DeclarationExpression expression) {
+ if (sameVariableName(leaf, expression.getLeftExpression())) {
+ Expression rightExpression = expression.getRightExpression();
+ if (rightExpression instanceof ConstantExpression
+ && !rightExpression.getText().equals("null")) { // NOI18N
+ guessedType = ((ConstantExpression) rightExpression).getType();
+ } else if (rightExpression instanceof ConstructorCallExpression) {
+ guessedType = ((ConstructorCallExpression) rightExpression).getType();
+ } else if (rightExpression instanceof MethodCallExpression) {
+ guessedType = MethodInference.findCallerType(rightExpression, path, doc, cursorOffset);
+ } else if (rightExpression instanceof StaticMethodCallExpression) {
+ guessedType = MethodInference.findCallerType(rightExpression, path, doc, cursorOffset);
+ } else if (rightExpression instanceof ListExpression) {
+ guessedType = ((ListExpression) rightExpression).getType();
+ }
}
+ }
+
+ @Override
+ public void visitVariableExpression(VariableExpression expression) {
+ if (expression.isSuperExpression()) {
+ guessedType = expression.getType().getSuperClass();
+ }
+ if (null != expression.getAccessedVariable()) {
+ Variable accessedVariable = expression.getAccessedVariable();
+
+ if (accessedVariable.hasInitialExpression()) {
+ Expression initialExpression = expression.getAccessedVariable().getInitialExpression();
+ if (initialExpression instanceof ConstantExpression
+ && !initialExpression.getText().equals("null")) { // NOI18N
+ guessedType = ((ConstantExpression) initialExpression).getType();
+ } else if (initialExpression instanceof ConstructorCallExpression) {
+ guessedType = ClassHelper.make(((ConstructorCallExpression) initialExpression).getType().getName());
+ } else if (initialExpression instanceof MethodCallExpression) {
+ int newOffset = ASTUtils.getOffset(doc, initialExpression.getLineNumber(), initialExpression.getColumnNumber());
+ AstPath newPath = new AstPath(path.root(), newOffset, doc);
+ guessedType = MethodInference.findCallerType(initialExpression, newPath, doc, newOffset);
+ }
+ } else if (accessedVariable instanceof Parameter) {
+ Parameter param = (Parameter) accessedVariable;
+ guessedType = param.getType();
+ }
+ } else if (!expression.getType().getName().equals("java.lang.Object")) {
+ guessedType = expression.getType();
+
+ }
super.visitVariableExpression(expression);
}
@@ -99,7 +168,11 @@ public class TypeInferenceVisitor extends TypeVisitor {
&& !rightExpression.getText().equals("null")) { // NOI18N
guessedType = ((ConstantExpression) rightExpression).getType();
} else if (rightExpression instanceof ConstructorCallExpression) {
- guessedType = ((ConstructorCallExpression) rightExpression).getType();
+ guessedType = ClassHelper.make(((ConstructorCallExpression) rightExpression).getType().getName());
+ } else if (rightExpression instanceof MethodCallExpression) {
+ guessedType = MethodInference.findCallerType(rightExpression, path, doc, cursorOffset);
+ } else if (rightExpression instanceof StaticMethodCallExpression) {
+ guessedType = MethodInference.findCallerType(rightExpression, path, doc, cursorOffset);
}
}
}
@@ -115,5 +188,10 @@ public class TypeInferenceVisitor extends TypeVisitor {
return node1 instanceof VariableExpression && node2 instanceof VariableExpression
&& ((VariableExpression) node1).getName().equals(((VariableExpression) node2).getName());
}
+
+ private static boolean sameVariableName(ASTNode node1, FieldNode node2) {
+ return node1 instanceof VariableExpression
+ && ((VariableExpression) node1).getName().equals(node2.getName());
+ }
}
diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/MetaElementsProvider.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/MetaElementsProvider.java
index f04648f..79994e2 100644
--- a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/MetaElementsProvider.java
+++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/MetaElementsProvider.java
@@ -28,6 +28,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.groovy.reflection.CachedClass;
+import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.modules.groovy.editor.api.completion.CompletionItem.MetaMethodItem;
import org.netbeans.modules.groovy.editor.api.completion.FieldSignature;
import org.netbeans.modules.groovy.editor.api.completion.MethodSignature;
@@ -52,29 +53,47 @@ public final class MetaElementsProvider implements CompletionProvider {
@Override
public Map<MethodSignature, CompletionItem> getMethods(CompletionContext context) {
final Map<MethodSignature, CompletionItem> result = new HashMap<MethodSignature, CompletionItem>();
- final Class clz = loadClass(context.getTypeName());
+ final Class clz = loadClass(context);
if (clz != null) {
final MetaClass metaClz = GroovySystem.getMetaClassRegistry().getMetaClass(clz);
if (metaClz != null) {
for (MetaMethod method : metaClz.getMetaMethods()) {
- populateProposal(clz, method, context.getPrefix(), context.getAnchor(), result, context.isNameOnly());
+ if (!method.isStatic()) {
+ populateProposal(clz, method, context.getPrefix(), context.getAnchor(), result, context.isNameOnly());
+ }
}
}
+ GroovySystem.getMetaClassRegistry().removeMetaClass(clz);
}
return result;
}
@Override
public Map<MethodSignature, CompletionItem> getStaticMethods(CompletionContext context) {
- return Collections.emptyMap();
+ final Map<MethodSignature, CompletionItem> result = new HashMap<MethodSignature, CompletionItem>();
+ final Class clz = loadClass(context);
+
+ if (clz != null) {
+ final MetaClass metaClz = GroovySystem.getMetaClassRegistry().getMetaClass(clz);
+
+ if (metaClz != null) {
+ for (MetaMethod method : metaClz.getMetaMethods()) {
+ if (method.isStatic()) {
+ populateProposal(clz, method, context.getPrefix(), context.getAnchor(), result, context.isNameOnly());
+ }
+ }
+ }
+ GroovySystem.getMetaClassRegistry().removeMetaClass(clz);
+ }
+ return result;
}
@Override
public Map<FieldSignature, CompletionItem> getFields(CompletionContext context) {
final Map<FieldSignature, CompletionItem> result = new HashMap<FieldSignature, CompletionItem>();
- final Class clazz = loadClass(context.getTypeName());
+ final Class clazz = loadClass(context);
if (clazz != null) {
final MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(clazz);
@@ -91,6 +110,7 @@ public final class MetaElementsProvider implements CompletionProvider {
context.getAnchor()));
}
}
+ GroovySystem.getMetaClassRegistry().removeMetaClass(clazz);
}
}
@@ -102,6 +122,16 @@ public final class MetaElementsProvider implements CompletionProvider {
return Collections.emptyMap();
}
+ private Class loadClass(CompletionContext context) {
+ try {
+ return context.getSurroundingClass().getCompileUnit().getClassLoader().loadClass(context.getTypeName());
+ } catch (ClassNotFoundException cnfe) {
+ return loadClass(context.getTypeName());
+ } catch (RuntimeException exception) {
+ return null;
+ }
+ }
+
private Class loadClass(String className) {
try {
// FIXME should be loaded by classpath classloader
@@ -226,6 +256,10 @@ public final class MetaElementsProvider implements CompletionProvider {
return false;
}
}
- return false;
+
+ //preferMethodsAccording to direct inheritance
+
+ return currentMethod.getDeclaringClass().isAssignableFrom(methodToStore.getDeclaringClass().getTheClass());
+
}
}
diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/occurrences/TypeVisitor.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/occurrences/TypeVisitor.java
index 7c623d5..2952ccc 100644
--- a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/occurrences/TypeVisitor.java
+++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/occurrences/TypeVisitor.java
@@ -23,6 +23,7 @@ import java.util.Iterator;
import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ClosureListExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ForStatement;
import org.codehaus.groovy.control.SourceUnit;
@@ -139,6 +140,9 @@ public class TypeVisitor extends ClassCodeVisitorSupport {
return;
}
}
+ else if (scope instanceof VariableExpression) {
+ visitVariableExpression((VariableExpression)scope);
+ }
}
}
diff --git a/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy
new file mode 100644
index 0000000..190a831
--- /dev/null
+++ b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy
@@ -0,0 +1,33 @@
+class Klazz {
+
+ def fieldA = new String("Hello")
+ def fieldA1 = "Hello"
+ def fieldB = fieldA.c
+ def fieldB1 = fieldA1.c
+ def fieldC = fieldA.concat("b").c
+ def fieldC1 = fieldA1.concat("b").c
+ def fieldD = "hi"
+ def fieldE = fieldA.concat(fieldD)
+ def fieldE1 = fieldA1.concat(fieldD)
+ def fieldF = fieldE.c
+ def fieldF1 = fieldE1.c
+
+ def m() {
+
+ def localA = "Hello"
+ localA.c
+
+ localA.concat("b").c
+
+ def localB = "hi"
+
+ localA.concat(localB).c
+
+
+ def localC = localA.concat(localB)
+
+ localC.c
+
+ }
+
+}
\ No newline at end of file
diff --git a/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_1.completion b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_1.completion
new file mode 100644
index 0000000..4fde144
--- /dev/null
+++ b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_1.completion
@@ -0,0 +1,29 @@
+Code completion result for source line:
+def fieldB = fieldA.c|
+(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
+------------------------------------
+METHOD capitalize() String
+METHOD center(Number) String
+METHOD center(Number, CharSequence) String
+METHOD charAt(int) [PUBLIC] char
+METHOD chars() [PUBLIC] IntStream
+METHOD codePointAt(int) [PUBLIC] int
+METHOD codePointBefore(int) [PUBLIC] int
+METHOD codePointCount(int, int) [PUBLIC] int
+METHOD codePoints() [PUBLIC] IntStream
+METHOD collect() Collection
+METHOD collect(Closure) List
+METHOD collect(Collection, Closure) Collection
+METHOD collectReplacements(Closure) String
+METHOD compareTo(String) [PUBLIC] int
+METHOD compareTo(T) [PUBLIC] int
+METHOD compareToIgnoreCase(String) [PUBLIC] int
+METHOD concat(String) [PUBLIC] String
+METHOD contains(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(StringBuffer) [PUBLIC] boolean
+METHOD copyValueOf(char[]) [STATIC, String
+METHOD copyValueOf(char[], int, int) [STATIC, String
+METHOD count(CharSequence) int
+FIELD CASE_INSENSITIVE_ORDER [STATIC, Comparator<String>
+FIELD class [PUBLIC] Class
diff --git a/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_2.completion b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_2.completion
new file mode 100644
index 0000000..53463bc
--- /dev/null
+++ b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_2.completion
@@ -0,0 +1,29 @@
+Code completion result for source line:
+def fieldB1 = fieldA1.c|
+(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
+------------------------------------
+METHOD capitalize() String
+METHOD center(Number) String
+METHOD center(Number, CharSequence) String
+METHOD charAt(int) [PUBLIC] char
+METHOD chars() [PUBLIC] IntStream
+METHOD codePointAt(int) [PUBLIC] int
+METHOD codePointBefore(int) [PUBLIC] int
+METHOD codePointCount(int, int) [PUBLIC] int
+METHOD codePoints() [PUBLIC] IntStream
+METHOD collect() Collection
+METHOD collect(Closure) List
+METHOD collect(Collection, Closure) Collection
+METHOD collectReplacements(Closure) String
+METHOD compareTo(String) [PUBLIC] int
+METHOD compareTo(T) [PUBLIC] int
+METHOD compareToIgnoreCase(String) [PUBLIC] int
+METHOD concat(String) [PUBLIC] String
+METHOD contains(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(StringBuffer) [PUBLIC] boolean
+METHOD copyValueOf(char[]) [STATIC, String
+METHOD copyValueOf(char[], int, int) [STATIC, String
+METHOD count(CharSequence) int
+FIELD CASE_INSENSITIVE_ORDER [STATIC, Comparator<String>
+FIELD class [PUBLIC] Class
diff --git a/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_3.completion b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_3.completion
new file mode 100644
index 0000000..5fd3523
--- /dev/null
+++ b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_3.completion
@@ -0,0 +1,29 @@
+Code completion result for source line:
+def fieldF = fieldE.c|
+(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
+------------------------------------
+METHOD capitalize() String
+METHOD center(Number) String
+METHOD center(Number, CharSequence) String
+METHOD charAt(int) [PUBLIC] char
+METHOD chars() [PUBLIC] IntStream
+METHOD codePointAt(int) [PUBLIC] int
+METHOD codePointBefore(int) [PUBLIC] int
+METHOD codePointCount(int, int) [PUBLIC] int
+METHOD codePoints() [PUBLIC] IntStream
+METHOD collect() Collection
+METHOD collect(Closure) List
+METHOD collect(Collection, Closure) Collection
+METHOD collectReplacements(Closure) String
+METHOD compareTo(String) [PUBLIC] int
+METHOD compareTo(T) [PUBLIC] int
+METHOD compareToIgnoreCase(String) [PUBLIC] int
+METHOD concat(String) [PUBLIC] String
+METHOD contains(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(StringBuffer) [PUBLIC] boolean
+METHOD copyValueOf(char[]) [STATIC, String
+METHOD copyValueOf(char[], int, int) [STATIC, String
+METHOD count(CharSequence) int
+FIELD CASE_INSENSITIVE_ORDER [STATIC, Comparator<String>
+FIELD class [PUBLIC] Class
diff --git a/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_4.completion b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_4.completion
new file mode 100644
index 0000000..493a7a6
--- /dev/null
+++ b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_4.completion
@@ -0,0 +1,29 @@
+Code completion result for source line:
+localA.c|
+(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
+------------------------------------
+METHOD capitalize() String
+METHOD center(Number) String
+METHOD center(Number, CharSequence) String
+METHOD charAt(int) [PUBLIC] char
+METHOD chars() [PUBLIC] IntStream
+METHOD codePointAt(int) [PUBLIC] int
+METHOD codePointBefore(int) [PUBLIC] int
+METHOD codePointCount(int, int) [PUBLIC] int
+METHOD codePoints() [PUBLIC] IntStream
+METHOD collect() Collection
+METHOD collect(Closure) List
+METHOD collect(Collection, Closure) Collection
+METHOD collectReplacements(Closure) String
+METHOD compareTo(String) [PUBLIC] int
+METHOD compareTo(T) [PUBLIC] int
+METHOD compareToIgnoreCase(String) [PUBLIC] int
+METHOD concat(String) [PUBLIC] String
+METHOD contains(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(StringBuffer) [PUBLIC] boolean
+METHOD copyValueOf(char[]) [STATIC, String
+METHOD copyValueOf(char[], int, int) [STATIC, String
+METHOD count(CharSequence) int
+FIELD CASE_INSENSITIVE_ORDER [STATIC, Comparator<String>
+FIELD class [PUBLIC] Class
diff --git a/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_5.completion b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_5.completion
new file mode 100644
index 0000000..492308d
--- /dev/null
+++ b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_5.completion
@@ -0,0 +1,29 @@
+Code completion result for source line:
+localA.concat("b").c|
+(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
+------------------------------------
+METHOD capitalize() String
+METHOD center(Number) String
+METHOD center(Number, CharSequence) String
+METHOD charAt(int) [PUBLIC] char
+METHOD chars() [PUBLIC] IntStream
+METHOD codePointAt(int) [PUBLIC] int
+METHOD codePointBefore(int) [PUBLIC] int
+METHOD codePointCount(int, int) [PUBLIC] int
+METHOD codePoints() [PUBLIC] IntStream
+METHOD collect() Collection
+METHOD collect(Closure) List
+METHOD collect(Collection, Closure) Collection
+METHOD collectReplacements(Closure) String
+METHOD compareTo(String) [PUBLIC] int
+METHOD compareTo(T) [PUBLIC] int
+METHOD compareToIgnoreCase(String) [PUBLIC] int
+METHOD concat(String) [PUBLIC] String
+METHOD contains(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(StringBuffer) [PUBLIC] boolean
+METHOD copyValueOf(char[]) [STATIC, String
+METHOD copyValueOf(char[], int, int) [STATIC, String
+METHOD count(CharSequence) int
+FIELD CASE_INSENSITIVE_ORDER [STATIC, Comparator<String>
+FIELD class [PUBLIC] Class
diff --git a/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_6.completion b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_6.completion
new file mode 100644
index 0000000..b471cd1
--- /dev/null
+++ b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_6.completion
@@ -0,0 +1,29 @@
+Code completion result for source line:
+localA.concat(localB).c|
+(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
+------------------------------------
+METHOD capitalize() String
+METHOD center(Number) String
+METHOD center(Number, CharSequence) String
+METHOD charAt(int) [PUBLIC] char
+METHOD chars() [PUBLIC] IntStream
+METHOD codePointAt(int) [PUBLIC] int
+METHOD codePointBefore(int) [PUBLIC] int
+METHOD codePointCount(int, int) [PUBLIC] int
+METHOD codePoints() [PUBLIC] IntStream
+METHOD collect() Collection
+METHOD collect(Closure) List
+METHOD collect(Collection, Closure) Collection
+METHOD collectReplacements(Closure) String
+METHOD compareTo(String) [PUBLIC] int
+METHOD compareTo(T) [PUBLIC] int
+METHOD compareToIgnoreCase(String) [PUBLIC] int
+METHOD concat(String) [PUBLIC] String
+METHOD contains(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(StringBuffer) [PUBLIC] boolean
+METHOD copyValueOf(char[]) [STATIC, String
+METHOD copyValueOf(char[], int, int) [STATIC, String
+METHOD count(CharSequence) int
+FIELD CASE_INSENSITIVE_ORDER [STATIC, Comparator<String>
+FIELD class [PUBLIC] Class
diff --git a/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_7.completion b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_7.completion
new file mode 100644
index 0000000..b210308
--- /dev/null
+++ b/groovy/groovy.editor/test/unit/data/testfiles/completion/flow/completionReturnType1/CompletionReturnType1.groovy.testCompletionReturnType1_7.completion
@@ -0,0 +1,29 @@
+Code completion result for source line:
+localC.c|
+(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
+------------------------------------
+METHOD capitalize() String
+METHOD center(Number) String
+METHOD center(Number, CharSequence) String
+METHOD charAt(int) [PUBLIC] char
+METHOD chars() [PUBLIC] IntStream
+METHOD codePointAt(int) [PUBLIC] int
+METHOD codePointBefore(int) [PUBLIC] int
+METHOD codePointCount(int, int) [PUBLIC] int
+METHOD codePoints() [PUBLIC] IntStream
+METHOD collect() Collection
+METHOD collect(Closure) List
+METHOD collect(Collection, Closure) Collection
+METHOD collectReplacements(Closure) String
+METHOD compareTo(String) [PUBLIC] int
+METHOD compareTo(T) [PUBLIC] int
+METHOD compareToIgnoreCase(String) [PUBLIC] int
+METHOD concat(String) [PUBLIC] String
+METHOD contains(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(CharSequence) [PUBLIC] boolean
+METHOD contentEquals(StringBuffer) [PUBLIC] boolean
+METHOD copyValueOf(char[]) [STATIC, String
+METHOD copyValueOf(char[], int, int) [STATIC, String
+METHOD count(CharSequence) int
+FIELD CASE_INSENSITIVE_ORDER [STATIC, Comparator<String>
+FIELD class [PUBLIC] Class
diff --git a/groovy/groovy.editor/test/unit/src/org/netbeans/modules/groovy/editor/api/completion/FlowCCTest.java b/groovy/groovy.editor/test/unit/src/org/netbeans/modules/groovy/editor/api/completion/FlowCCTest.java
new file mode 100644
index 0000000..dda6975
--- /dev/null
+++ b/groovy/groovy.editor/test/unit/src/org/netbeans/modules/groovy/editor/api/completion/FlowCCTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.modules.groovy.editor.api.completion;
+
+/**
+ *
+ * @author sreimers
+ */
+public class FlowCCTest extends GroovyCCTestBase {
+
+ public FlowCCTest(String testName) {
+ super(testName);
+ }
+
+ @Override
+ protected String getTestType() {
+ return "flow"; //NOI18N
+ }
+
+ public void testCompletionReturnType1_1() throws Exception {
+ checkCompletion(BASE + "CompletionReturnType1.groovy", "def fieldB = fieldA.c^", false);
+ }
+
+ public void testCompletionReturnType1_2() throws Exception {
+ checkCompletion(BASE + "CompletionReturnType1.groovy", "def fieldB1 = fieldA1.c^", false);
+ }
+
+ public void testCompletionReturnType1_3() throws Exception {
+ checkCompletion(BASE + "CompletionReturnType1.groovy", "fieldE.c^", false);
+ }
+
+ public void testCompletionReturnType1_4() throws Exception {
+ checkCompletion(BASE + "CompletionReturnType1.groovy", "localA.c^", false);
+ }
+
+ public void testCompletionReturnType1_5() throws Exception {
+ checkCompletion(BASE + "CompletionReturnType1.groovy", "localA.concat(\"b\").c^", false);
+ }
+
+ public void testCompletionReturnType1_6() throws Exception {
+ checkCompletion(BASE + "CompletionReturnType1.groovy", "localA.concat(localB).c^", false);
+ }
+
+ public void testCompletionReturnType1_7() throws Exception {
+ checkCompletion(BASE + "CompletionReturnType1.groovy", "localC.c^", false);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists