diff --git a/base/org.codehaus.groovy24/src/org/codehaus/groovy/classgen/EnumVisitor.java b/base/org.codehaus.groovy24/src/org/codehaus/groovy/classgen/EnumVisitor.java index 12ce8c082d..bbcb48c3d7 100644 --- a/base/org.codehaus.groovy24/src/org/codehaus/groovy/classgen/EnumVisitor.java +++ b/base/org.codehaus.groovy24/src/org/codehaus/groovy/classgen/EnumVisitor.java @@ -150,7 +150,7 @@ private static void addMethods(ClassNode enumClass, FieldNode values) { // } Token assign = Token.newSymbol(Types.ASSIGN, -1, -1); Token ge = Token.newSymbol(Types.COMPARE_GREATER_THAN_EQUAL, -1, -1); - MethodNode nextMethod = new MethodNode("next", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); + MethodNode nextMethod = new MethodNode("next", Opcodes.ACC_PUBLIC/*GRECLIPSE | Opcodes.ACC_SYNTHETIC*/, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); nextMethod.setSynthetic(true); BlockStatement code = new BlockStatement(); BlockStatement ifStatement = new BlockStatement(); @@ -209,7 +209,7 @@ private static void addMethods(ClassNode enumClass, FieldNode values) { // } Token assign = Token.newSymbol(Types.ASSIGN, -1, -1); Token lt = Token.newSymbol(Types.COMPARE_LESS_THAN, -1, -1); - MethodNode nextMethod = new MethodNode("previous", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); + MethodNode nextMethod = new MethodNode("previous", Opcodes.ACC_PUBLIC/*GRECLIPSE | Opcodes.ACC_SYNTHETIC*/, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); nextMethod.setSynthetic(true); BlockStatement code = new BlockStatement(); BlockStatement ifStatement = new BlockStatement(); diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/EnumVisitor.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/EnumVisitor.java index 2143fec92e..b77a07a34d 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/EnumVisitor.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/EnumVisitor.java @@ -152,7 +152,7 @@ private static void addMethods(ClassNode enumClass, FieldNode values) { // } Token assign = Token.newSymbol(Types.ASSIGN, -1, -1); Token ge = Token.newSymbol(Types.COMPARE_GREATER_THAN_EQUAL, -1, -1); - MethodNode nextMethod = new MethodNode("next", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); + MethodNode nextMethod = new MethodNode("next", Opcodes.ACC_PUBLIC/*GRECLIPSE | Opcodes.ACC_SYNTHETIC*/, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); nextMethod.setSynthetic(true); BlockStatement code = new BlockStatement(); BlockStatement ifStatement = new BlockStatement(); @@ -211,7 +211,7 @@ private static void addMethods(ClassNode enumClass, FieldNode values) { // } Token assign = Token.newSymbol(Types.ASSIGN, -1, -1); Token lt = Token.newSymbol(Types.COMPARE_LESS_THAN, -1, -1); - MethodNode nextMethod = new MethodNode("previous", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); + MethodNode nextMethod = new MethodNode("previous", Opcodes.ACC_PUBLIC/*GRECLIPSE | Opcodes.ACC_SYNTHETIC*/, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); nextMethod.setSynthetic(true); BlockStatement code = new BlockStatement(); BlockStatement ifStatement = new BlockStatement(); diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/EnumVisitor.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/EnumVisitor.java index 2143fec92e..13a1469115 100644 --- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/EnumVisitor.java +++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/EnumVisitor.java @@ -56,69 +56,71 @@ import org.codehaus.groovy.syntax.PreciseSyntaxException; import org.codehaus.groovy.syntax.Token; import org.codehaus.groovy.syntax.Types; -import groovyjarjarasm.asm.Opcodes; import java.util.ArrayList; import java.util.List; +import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod; import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX; +import static groovyjarjarasm.asm.Opcodes.ACC_ABSTRACT; +import static groovyjarjarasm.asm.Opcodes.ACC_FINAL; +import static groovyjarjarasm.asm.Opcodes.ACC_PRIVATE; +import static groovyjarjarasm.asm.Opcodes.ACC_PUBLIC; +import static groovyjarjarasm.asm.Opcodes.ACC_STATIC; +import static groovyjarjarasm.asm.Opcodes.ACC_SYNTHETIC; public class EnumVisitor extends ClassCodeVisitorSupport { - // some constants for modifiers - private static final int FS = Opcodes.ACC_FINAL | Opcodes.ACC_STATIC; - private static final int PS = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC; - private static final int PUBLIC_FS = Opcodes.ACC_PUBLIC | FS; - private static final int PRIVATE_FS = Opcodes.ACC_PRIVATE | FS; private final SourceUnit sourceUnit; - public EnumVisitor(CompilationUnit cu, SourceUnit su) { + public EnumVisitor(final CompilationUnit cu, final SourceUnit su) { sourceUnit = su; } - public void visitClass(ClassNode node) { - if (!node.isEnum()) return; - completeEnum(node); - } - + @Override protected SourceUnit getSourceUnit() { return sourceUnit; } - private void completeEnum(ClassNode enumClass) { - boolean isAic = isAnonymousInnerClass(enumClass); - // create MIN_VALUE and MAX_VALUE fields + @Override + public void visitClass(final ClassNode node) { + if (!node.isEnum()) return; + completeEnum(node); + } + + private void completeEnum(final ClassNode enumClass) { FieldNode minValue = null, maxValue = null, values = null; + boolean isAic = isAnonymousInnerClass(enumClass); if (!isAic) { ClassNode enumRef = enumClass.getPlainNodeReference(); - // create values field - values = new FieldNode("$VALUES", PRIVATE_FS | Opcodes.ACC_SYNTHETIC, enumRef.makeArray(), enumClass, null); + // create $VALUES field + values = new FieldNode("$VALUES", ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, enumRef.makeArray(), enumClass, null); values.setSynthetic(true); addMethods(enumClass, values); checkForAbstractMethods(enumClass); // create MIN_VALUE and MAX_VALUE fields - minValue = new FieldNode("MIN_VALUE", PUBLIC_FS, enumRef, enumClass, null); - maxValue = new FieldNode("MAX_VALUE", PUBLIC_FS, enumRef, enumClass, null); + minValue = new FieldNode("MIN_VALUE", ACC_FINAL | ACC_PUBLIC | ACC_STATIC, enumRef, enumClass, null); + maxValue = new FieldNode("MAX_VALUE", ACC_FINAL | ACC_PUBLIC | ACC_STATIC, enumRef, enumClass, null); } + addInit(enumClass, minValue, maxValue, values, isAic); } - private static void checkForAbstractMethods(ClassNode enumClass) { - List methods = enumClass.getMethods(); - for (MethodNode m : methods) { - if (m.isAbstract()) { - // make the class abstract also see Effective Java p.152 - enumClass.setModifiers(enumClass.getModifiers() | Opcodes.ACC_ABSTRACT); + private static void checkForAbstractMethods(final ClassNode enumClass) { + for (MethodNode method : enumClass.getMethods()) { + if (method.isAbstract()) { + // make the class abstract also; see Effective Java p.152 + enumClass.setModifiers(enumClass.getModifiers() | ACC_ABSTRACT); break; } } } - private static void addMethods(ClassNode enumClass, FieldNode values) { + private static void addMethods(final ClassNode enumClass, final FieldNode values) { List methods = enumClass.getMethods(); boolean hasNext = false; @@ -133,14 +135,14 @@ private static void addMethods(ClassNode enumClass, FieldNode values) { { // create values() method - MethodNode valuesMethod = new MethodNode("values", PUBLIC_FS, enumRef.makeArray(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); + MethodNode valuesMethod = new MethodNode("values", ACC_FINAL | ACC_PUBLIC | ACC_STATIC, enumRef.makeArray(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); valuesMethod.setSynthetic(true); BlockStatement code = new BlockStatement(); MethodCallExpression cloneCall = new MethodCallExpression(new FieldExpression(values), "clone", MethodCallExpression.NO_ARGUMENTS); cloneCall.setMethodTarget(values.getType().getMethod("clone", Parameter.EMPTY_ARRAY)); code.addStatement(new ReturnStatement(cloneCall)); valuesMethod.setCode(code); - enumClass.addMethod(valuesMethod); + addGeneratedMethod(enumClass, valuesMethod); } if (!hasNext) { @@ -152,7 +154,7 @@ private static void addMethods(ClassNode enumClass, FieldNode values) { // } Token assign = Token.newSymbol(Types.ASSIGN, -1, -1); Token ge = Token.newSymbol(Types.COMPARE_GREATER_THAN_EQUAL, -1, -1); - MethodNode nextMethod = new MethodNode("next", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); + MethodNode nextMethod = new MethodNode("next", ACC_PUBLIC/*GRECLIPSE | ACC_SYNTHETIC*/, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); nextMethod.setSynthetic(true); BlockStatement code = new BlockStatement(); BlockStatement ifStatement = new BlockStatement(); @@ -199,7 +201,7 @@ private static void addMethods(ClassNode enumClass, FieldNode values) { ) ); nextMethod.setCode(code); - enumClass.addMethod(nextMethod); + addGeneratedMethod(enumClass, nextMethod); } if (!hasPrevious) { @@ -211,8 +213,8 @@ private static void addMethods(ClassNode enumClass, FieldNode values) { // } Token assign = Token.newSymbol(Types.ASSIGN, -1, -1); Token lt = Token.newSymbol(Types.COMPARE_LESS_THAN, -1, -1); - MethodNode nextMethod = new MethodNode("previous", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); - nextMethod.setSynthetic(true); + MethodNode prevMethod = new MethodNode("previous", ACC_PUBLIC/*GRECLIPSE | ACC_SYNTHETIC*/, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); + prevMethod.setSynthetic(true); BlockStatement code = new BlockStatement(); BlockStatement ifStatement = new BlockStatement(); ifStatement.addStatement( @@ -263,14 +265,14 @@ private static void addMethods(ClassNode enumClass, FieldNode values) { new MethodCallExpression(new FieldExpression(values), "getAt", new VariableExpression("ordinal")) ) ); - nextMethod.setCode(code); - enumClass.addMethod(nextMethod); + prevMethod.setCode(code); + addGeneratedMethod(enumClass, prevMethod); } { // create valueOf Parameter stringParameter = new Parameter(ClassHelper.STRING_TYPE, "name"); - MethodNode valueOfMethod = new MethodNode("valueOf", PS, enumRef, new Parameter[]{stringParameter}, ClassNode.EMPTY_ARRAY, null); + MethodNode valueOfMethod = new MethodNode("valueOf", ACC_PUBLIC | ACC_STATIC, enumRef, new Parameter[]{stringParameter}, ClassNode.EMPTY_ARRAY, null); ArgumentListExpression callArguments = new ArgumentListExpression(); callArguments.addExpression(new ClassExpression(enumClass)); callArguments.addExpression(new VariableExpression("name")); @@ -283,19 +285,17 @@ private static void addMethods(ClassNode enumClass, FieldNode values) { ); valueOfMethod.setCode(code); valueOfMethod.setSynthetic(true); - enumClass.addMethod(valueOfMethod); + addGeneratedMethod(enumClass, valueOfMethod); } } - private void addInit(ClassNode enumClass, FieldNode minValue, - FieldNode maxValue, FieldNode values, - boolean isAic) { + private void addInit(final ClassNode enumClass, final FieldNode minValue, final FieldNode maxValue, final FieldNode values, final boolean isAic) { // constructor helper // This method is used instead of calling the constructor as // calling the constructor may require a table with MetaClass // selecting the constructor for each enum value. So instead we // use this method to have a central point for constructor selection - // and only one table. The whole construction is needed because + // and only one table. The whole construction is needed because // Reflection forbids access to the enum constructor. // code: // def $INIT(Object[] para) { @@ -303,7 +303,7 @@ private void addInit(ClassNode enumClass, FieldNode minValue, // } ClassNode enumRef = enumClass.getPlainNodeReference(); Parameter[] parameter = new Parameter[]{new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "para")}; - MethodNode initMethod = new MethodNode("$INIT", PUBLIC_FS | Opcodes.ACC_SYNTHETIC, enumRef, parameter, ClassNode.EMPTY_ARRAY, null); + MethodNode initMethod = new MethodNode("$INIT", ACC_FINAL | ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, enumRef, parameter, ClassNode.EMPTY_ARRAY, null); initMethod.setSynthetic(true); ConstructorCallExpression cce = new ConstructorCallExpression( ClassNode.THIS, @@ -314,19 +314,19 @@ private void addInit(ClassNode enumClass, FieldNode minValue, BlockStatement code = new BlockStatement(); code.addStatement(new ReturnStatement(cce)); initMethod.setCode(code); - enumClass.addMethod(initMethod); + addGeneratedMethod(enumClass, initMethod); // static init List fields = enumClass.getFields(); - List arrayInit = new ArrayList(); + List arrayInit = new ArrayList<>(); int value = -1; Token assign = Token.newSymbol(Types.ASSIGN, -1, -1); - List block = new ArrayList(); + List block = new ArrayList<>(); FieldNode tempMin = null; FieldNode tempMax = null; for (FieldNode field : fields) { - if ((field.getModifiers() & Opcodes.ACC_ENUM) == 0) continue; - value++; + if (!field.isEnum()) continue; + value += 1; if (tempMin == null) tempMin = field; tempMax = field; @@ -335,13 +335,13 @@ private void addInit(ClassNode enumClass, FieldNode minValue, args.addExpression(new ConstantExpression(field.getName())); args.addExpression(new ConstantExpression(value)); if (field.getInitialExpression() == null) { - if ((enumClass.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) { + if (enumClass.isAbstract()) { addError(field, "The enum constant " + field.getName() + " must override abstract methods from " + enumBase.getName() + "."); continue; } } else { ListExpression oldArgs = (ListExpression) field.getInitialExpression(); - List savedMapEntries = new ArrayList(); + List savedMapEntries = new ArrayList<>(); for (Expression exp : oldArgs.getExpressions()) { if (exp instanceof MapEntryExpression) { savedMapEntries.add((MapEntryExpression) exp); @@ -361,7 +361,7 @@ private void addInit(ClassNode enumClass, FieldNode minValue, for (MethodNode methodNode : baseMethods) { if (!methodNode.isAbstract()) continue; MethodNode enumConstMethod = inner.getMethod(methodNode.getName(), methodNode.getParameters()); - if (enumConstMethod == null || (enumConstMethod.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) { + if (enumConstMethod == null || enumConstMethod.isAbstract()) { addError(field, "Can't have an abstract method in enum constant " + field.getName() + ". Implement method '" + methodNode.getTypeDescriptor() + "'."); } } @@ -372,7 +372,7 @@ private void addInit(ClassNode enumClass, FieldNode minValue, * so that subclasses of enum generated for enum constants (aic) can provide * their own $INIT method */ - initMethod.setModifiers(initMethod.getModifiers() & ~Opcodes.ACC_FINAL); + initMethod.setModifiers(initMethod.getModifiers() & ~ACC_FINAL); continue; } } @@ -429,7 +429,7 @@ private void addInit(ClassNode enumClass, FieldNode minValue, enumClass.addStaticInitializerStatements(block, true); } - private void addError(AnnotatedNode exp, String msg) { + private void addError(final AnnotatedNode exp, final String msg) { // GRECLIPSE add int start = exp.getStart(), end = exp.getEnd(); if (exp instanceof FieldNode && ((FieldNode) exp).hasInitialExpression()) { @@ -438,18 +438,20 @@ private void addError(AnnotatedNode exp, String msg) { end = type.getStart() - 2; // cover arguments but not start of block } // GRECLIPSE end - sourceUnit.getErrorCollector().addErrorAndContinue( + getSourceUnit().getErrorCollector().addErrorAndContinue( new SyntaxErrorMessage( - // GRECLIPSE edit - //new SyntaxException(msg + '\n', exp.getLineNumber(), exp.getColumnNumber(), exp.getLastLineNumber(), exp.getLastColumnNumber()), sourceUnit) - new PreciseSyntaxException(msg + '\n', exp.getLineNumber(), exp.getColumnNumber(), start, end), sourceUnit) + /* GRECLIPSE edit + new SyntaxException(msg + '\n', exp), + */ + new PreciseSyntaxException(msg + '\n', exp.getLineNumber(), exp.getColumnNumber(), start, end), // GRECLIPSE end + getSourceUnit() + ) ); } - private static boolean isAnonymousInnerClass(ClassNode enumClass) { + private static boolean isAnonymousInnerClass(final ClassNode enumClass) { if (!(enumClass instanceof EnumConstantClassNode)) return false; - InnerClassNode ic = (InnerClassNode) enumClass; - return ic.getVariableScope() == null; + return (((EnumConstantClassNode) enumClass).getVariableScope() == null); } } diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.test/src/org/codehaus/groovy/eclipse/codeassist/tests/OtherCompletionTests.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.test/src/org/codehaus/groovy/eclipse/codeassist/tests/OtherCompletionTests.groovy index b1b3029c1e..f9f59dde7b 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.test/src/org/codehaus/groovy/eclipse/codeassist/tests/OtherCompletionTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.test/src/org/codehaus/groovy/eclipse/codeassist/tests/OtherCompletionTests.groovy @@ -204,6 +204,37 @@ final class OtherCompletionTests extends CompletionTestSuite { checkReplacementString(proposals, 'xx', 1) } + @Test // https://github.com/groovy/groovy-eclipse/issues/1004 + void testEnumCompletion1() { + String contents = 'enum E {F,G}\nE.F.n' + ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'n')) + checkReplacementString(proposals, 'next()', 2) + } + + @Test // https://github.com/groovy/groovy-eclipse/issues/1004 + void testEnumCompletion2() { + String contents = 'enum E {F,G}\nE.F.p' + ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'p')) + checkReplacementString(proposals, 'previous()', 2) + } + + @Test + void testEnumCompletion3() { + String contents = 'enum E {F,G}\nE.v' + ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'v')) + checkReplacementString(proposals, 'values()', 1) + checkReplacementString(proposals, 'valueOf(name)', 1) + checkReplacementString(proposals, 'valueOf(enumType, name)', 1) + } + + @Test + void testEnumCompletion4() { + String contents = 'enum E {F,G}\nE.M' + ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'M')) + checkReplacementString(proposals, 'MIN_VALUE', 1) + checkReplacementString(proposals, 'MAX_VALUE', 1) + } + @Test void testListCompletion1() { String contents = '[].'