From 55619e8030768fb42f8a9b4207bb330806eaceef Mon Sep 17 00:00:00 2001 From: Bulgakov Alexander Date: Thu, 20 Oct 2016 16:41:40 +0300 Subject: added supporting of @var variables. The @var annotation has the same functionality as the @val except 'final' modifier. --- src/core/lombok/ConfigurationKeys.java | 3 +- src/core/lombok/eclipse/handlers/HandleVal.java | 34 ++++--- src/core/lombok/javac/handlers/HandleVal.java | 89 ++++++++++-------- src/core/lombok/var.java | 28 ++++++ .../lombok/eclipse/agent/PatchVal.java | 104 +++++++++++---------- .../lombok/eclipse/agent/PatchValEclipse.java | 10 +- .../resource/after-delombok/VarComplex.java | 20 ++++ .../resource/after-delombok/VarInFor.java | 9 ++ .../resource/after-delombok/VarInForOld.java | 7 ++ .../resource/after-delombok/VarModifier.java | 7 ++ test/transform/resource/after-ecj/VarComplex.java | 26 ++++++ test/transform/resource/after-ecj/VarInFor.java | 14 +++ test/transform/resource/after-ecj/VarInForOld.java | 12 +++ test/transform/resource/after-ecj/VarModifier.java | 12 +++ test/transform/resource/before/VarComplex.java | 22 +++++ test/transform/resource/before/VarInFor.java | 11 +++ test/transform/resource/before/VarInForOld.java | 9 ++ test/transform/resource/before/VarModifier.java | 10 ++ 18 files changed, 321 insertions(+), 106 deletions(-) create mode 100644 src/core/lombok/var.java create mode 100644 test/transform/resource/after-delombok/VarComplex.java create mode 100644 test/transform/resource/after-delombok/VarInFor.java create mode 100644 test/transform/resource/after-delombok/VarInForOld.java create mode 100644 test/transform/resource/after-delombok/VarModifier.java create mode 100644 test/transform/resource/after-ecj/VarComplex.java create mode 100644 test/transform/resource/after-ecj/VarInFor.java create mode 100644 test/transform/resource/after-ecj/VarInForOld.java create mode 100644 test/transform/resource/after-ecj/VarModifier.java create mode 100644 test/transform/resource/before/VarComplex.java create mode 100644 test/transform/resource/before/VarInFor.java create mode 100644 test/transform/resource/before/VarInForOld.java create mode 100644 test/transform/resource/before/VarModifier.java diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 04decf69..132bde8d 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -283,7 +283,8 @@ public class ConfigurationKeys { * If set, any usage of {@code val} results in a warning / error. */ public static final ConfigurationKey VAL_FLAG_USAGE = new ConfigurationKey("lombok.val.flagUsage", "Emit a warning or error if 'val' is used.") {}; - + public static final ConfigurationKey VAR_FLAG_USAGE = new ConfigurationKey("lombok.var.flagUsage", "Emit a warning or error if 'var' is used.") {}; + // ##### Extern ##### // ----- Logging ----- diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java index d4ae417c..1f54bfac 100644 --- a/src/core/lombok/eclipse/handlers/HandleVal.java +++ b/src/core/lombok/eclipse/handlers/HandleVal.java @@ -22,6 +22,8 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.*; +import static lombok.eclipse.handlers.EclipseHandlerUtil.typeMatches; + import lombok.ConfigurationKeys; import lombok.val; import lombok.core.HandlerPriority; @@ -30,10 +32,12 @@ import lombok.eclipse.EclipseASTAdapter; import lombok.eclipse.EclipseASTVisitor; import lombok.eclipse.EclipseNode; +import lombok.var; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.ForStatement; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.mangosdk.spi.ProviderFor; /* @@ -44,33 +48,39 @@ import org.mangosdk.spi.ProviderFor; @HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated. public class HandleVal extends EclipseASTAdapter { @Override public void visitLocal(EclipseNode localNode, LocalDeclaration local) { - if (!EclipseHandlerUtil.typeMatches(val.class, localNode, local.type)) return; - handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); - + TypeReference type = local.type; + boolean isVal = typeMatches(val.class, localNode, type); + boolean isVar = typeMatches(var.class, localNode, type); + if (!(isVal ||isVar)) return; + + if (isVal) handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); + if (isVar) handleFlagUsage(localNode, ConfigurationKeys.VAR_FLAG_USAGE, "var"); + boolean variableOfForEach = false; - + if (localNode.directUp().get() instanceof ForeachStatement) { ForeachStatement fs = (ForeachStatement) localNode.directUp().get(); variableOfForEach = fs.elementVariable == local; } - + + String annotation = isVal ? "val" : "var"; if (local.initialization == null && !variableOfForEach) { - localNode.addError("'val' on a local variable requires an initializer expression"); + localNode.addError("'" + annotation + "' on a local variable requires an initializer expression"); return; } - + if (local.initialization instanceof ArrayInitializer) { - localNode.addError("'val' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); + localNode.addError("'" + annotation + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); return; } - - if (localNode.directUp().get() instanceof ForStatement) { + + if (isVal && localNode.directUp().get() instanceof ForStatement) { localNode.addError("'val' is not allowed in old-style for loops"); return; } - + if (local.initialization != null && local.initialization.getClass().getName().equals("org.eclipse.jdt.internal.compiler.ast.LambdaExpression")) { - localNode.addError("'val' is not allowed with lambda expressions."); + localNode.addError("'" + annotation + "' is not allowed with lambda expressions."); } } } diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 337ab2d7..f4c816d6 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -21,50 +21,52 @@ */ package lombok.javac.handlers; -import static lombok.core.handlers.HandlerUtil.*; -import static lombok.javac.handlers.JavacHandlerUtil.*; -import lombok.ConfigurationKeys; -import lombok.val; -import lombok.core.HandlerPriority; -import lombok.javac.JavacASTAdapter; -import lombok.javac.JavacASTVisitor; -import lombok.javac.JavacNode; -import lombok.javac.JavacResolution; -import lombok.javac.ResolutionResetNeeded; - -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.JCAnnotation; -import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; -import com.sun.tools.javac.tree.JCTree.JCExpression; -import com.sun.tools.javac.tree.JCTree.JCForLoop; -import com.sun.tools.javac.tree.JCTree.JCNewArray; -import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.util.List; +import lombok.ConfigurationKeys; +import lombok.core.HandlerPriority; +import lombok.javac.*; +import lombok.val; +import lombok.var; +import org.mangosdk.spi.ProviderFor; + +import static lombok.core.handlers.HandlerUtil.handleFlagUsage; +import static lombok.javac.handlers.JavacHandlerUtil.recursiveSetGeneratedBy; +import static lombok.javac.handlers.JavacHandlerUtil.typeMatches; @ProviderFor(JavacASTVisitor.class) @HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated. @ResolutionResetNeeded public class HandleVal extends JavacASTAdapter { - @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) { + + private static boolean eq(String typeTreeToString, String key) { + return (typeTreeToString.equals(key) || typeTreeToString.equals("lombok." + key)); + } + + @Override + public void visitLocal(JavacNode localNode, JCVariableDecl local) { JCTree typeTree = local.vartype; if (typeTree == null) return; String typeTreeToString = typeTree.toString(); - if (!typeTreeToString.equals("val") && !typeTreeToString.equals("lombok.val")) return; - if (!typeMatches(val.class, localNode, typeTree)) return; - - handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); - + + if (!(eq(typeTreeToString, "val") || eq(typeTreeToString, "var"))) return; + boolean isVal = typeMatches(val.class, localNode, typeTree); + boolean isVar = typeMatches(var.class, localNode, typeTree); + if (!(isVal || isVar)) return; + + if (isVal) handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); + if (isVar) handleFlagUsage(localNode, ConfigurationKeys.VAR_FLAG_USAGE, "var"); + JCTree parentRaw = localNode.directUp().get(); - if (parentRaw instanceof JCForLoop) { + if (isVal && parentRaw instanceof JCForLoop) { localNode.addError("'val' is not allowed in old-style for loops"); return; } - + JCExpression rhsOfEnhancedForLoop = null; if (local.init == null) { if (parentRaw instanceof JCEnhancedForLoop) { @@ -72,32 +74,37 @@ public class HandleVal extends JavacASTAdapter { if (efl.var == local) rhsOfEnhancedForLoop = efl.expr; } } - + + final String annotation = typeTreeToString; if (rhsOfEnhancedForLoop == null && local.init == null) { - localNode.addError("'val' on a local variable requires an initializer expression"); + localNode.addError("'" + annotation + "' on a local variable requires an initializer expression"); return; + } - + if (local.init instanceof JCNewArray && ((JCNewArray)local.init).elemtype == null) { - localNode.addError("'val' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); + localNode.addError("'" + annotation + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); return; } - - if (localNode.shouldDeleteLombokAnnotations()) JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, "lombok.val"); - - local.mods.flags |= Flags.FINAL; - + + if (localNode.shouldDeleteLombokAnnotations()) { + JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, "lombok.val"); + JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, "lombok.var"); + } + + if (isVal) local.mods.flags |= Flags.FINAL; + if (!localNode.shouldDeleteLombokAnnotations()) { JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.nil()), typeTree, localNode.getContext()); local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation); } - + if (JavacResolution.platformHasTargetTyping()) { local.vartype = localNode.getAst().getTreeMaker().Ident(localNode.getAst().toName("___Lombok_VAL_Attrib__")); } else { local.vartype = JavacResolution.createJavaLangObject(localNode.getAst()); } - + Type type; try { if (rhsOfEnhancedForLoop == null) { @@ -130,10 +137,10 @@ public class HandleVal extends JavacASTAdapter { type = rhsOfEnhancedForLoop.type; } } - + try { JCExpression replacement; - + if (rhsOfEnhancedForLoop != null) { Type componentType = JavacResolution.ifTypeIsIterableToComponent(type, localNode.getAst()); if (componentType == null) replacement = JavacResolution.createJavaLangObject(localNode.getAst()); @@ -141,7 +148,7 @@ public class HandleVal extends JavacASTAdapter { } else { replacement = JavacResolution.typeToJCTree(type, localNode.getAst(), false); } - + if (replacement != null) { local.vartype = replacement; } else { diff --git a/src/core/lombok/var.java b/src/core/lombok/var.java new file mode 100644 index 00000000..397f2760 --- /dev/null +++ b/src/core/lombok/var.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010-2013 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok; + +/** + * like val but not final + */ +public @interface var { +} diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index 2b8dfbaa..4a43c873 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -21,11 +21,6 @@ */ package lombok.eclipse.agent; -import static lombok.eclipse.handlers.EclipseHandlerUtil.*; -import static lombok.eclipse.Eclipse.*; - -import java.lang.reflect.Field; - import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; @@ -43,14 +38,19 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import java.lang.reflect.Field; + +import static lombok.eclipse.Eclipse.poss; +import static lombok.eclipse.handlers.EclipseHandlerUtil.makeType; + public class PatchVal { - + // This is half of the work for 'val' support - the other half is in PatchValEclipse. This half is enough for ecj. // Creates a copy of the 'initialization' field on a LocalDeclaration if the type of the LocalDeclaration is 'val', because the completion parser will null this out, // which in turn stops us from inferring the intended type for 'val x = 5;'. We look at the copy. // Also patches local declaration to not call .resolveType() on the initializer expression if we've already done so (calling it twice causes weird errors), // and patches .resolve() on LocalDeclaration itself to just-in-time replace the 'val' vartype with the right one. - + public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) { if (expr.resolvedType != null) return expr.resolvedType; try { @@ -62,7 +62,7 @@ public class PatchVal { return null; } } - + public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) { if (decl != null && LocalDeclaration.class.equals(decl.getClass()) && expr.resolvedType != null) return expr.resolvedType; try { @@ -74,56 +74,56 @@ public class PatchVal { return null; } } - + public static boolean matches(String key, char[] array) { if (array == null || key.length() != array.length) return false; for (int i = 0; i < array.length; i++) { if (key.charAt(i) != array[i]) return false; } - + return true; } - - public static boolean couldBeVal(TypeReference ref) { + + public static boolean couldBe(String key, TypeReference ref) { if (ref instanceof SingleTypeReference) { char[] token = ((SingleTypeReference)ref).token; - return matches("val", token); + return matches(key, token); } - + if (ref instanceof QualifiedTypeReference) { char[][] tokens = ((QualifiedTypeReference)ref).tokens; if (tokens == null || tokens.length != 2) return false; - return matches("lombok", tokens[0]) && matches("val", tokens[1]); + return matches("lombok", tokens[0]) && matches(key, tokens[1]); } - + return false; } - - private static boolean isVal(TypeReference ref, BlockScope scope) { - if (!couldBeVal(ref)) return false; - + + private static boolean is(TypeReference ref, BlockScope scope, String key) { + if (!couldBe(key, ref)) return false; + TypeBinding resolvedType = ref.resolvedType; if (resolvedType == null) resolvedType = ref.resolveType(scope, false); if (resolvedType == null) return false; - + char[] pkg = resolvedType.qualifiedPackageName(); char[] nm = resolvedType.qualifiedSourceName(); - return matches("lombok", pkg) && matches("val", nm); + return matches("lombok", pkg) && matches(key, nm); } - + public static final class Reflection { private static final Field initCopyField, iterableCopyField; - + static { Field a = null, b = null; - + try { a = LocalDeclaration.class.getDeclaredField("$initCopy"); b = LocalDeclaration.class.getDeclaredField("$iterableCopy"); } catch (Throwable t) { //ignore - no $initCopy exists when running in ecj. } - + initCopyField = a; iterableCopyField = b; } @@ -131,18 +131,22 @@ public class PatchVal { public static boolean handleValForLocalDeclaration(LocalDeclaration local, BlockScope scope) { if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false; boolean decomponent = false; - - if (!isVal(local.type, scope)) return false; - + + boolean val = is(local.type, scope, "val"); + boolean var = is(local.type, scope, "var"); + if (!(val || var)) return false; + StackTraceElement[] st = new Throwable().getStackTrace(); for (int i = 0; i < st.length - 2 && i < 10; i++) { if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) { - if (st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && - st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement")) return false; + boolean valInForStatement = val && + st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && + st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement"); + if (valInForStatement) return false; break; } } - + Expression init = local.initialization; if (init == null && Reflection.initCopyField != null) { try { @@ -151,7 +155,7 @@ public class PatchVal { // init remains null. } } - + if (init == null && Reflection.iterableCopyField != null) { try { init = (Expression) Reflection.iterableCopyField.get(local); @@ -186,8 +190,8 @@ public class PatchVal { } } } - - local.modifiers |= ClassFileConstants.AccFinal; + + if(val) local.modifiers |= ClassFileConstants.AccFinal; local.annotations = addValAnnotation(local.annotations, local.type, scope); local.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(local.type, 3)); @@ -196,21 +200,23 @@ public class PatchVal { public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) { if (forEach.elementVariable == null) return false; - - if (!isVal(forEach.elementVariable.type, scope)) return false; - + + boolean val = is(forEach.elementVariable.type, scope, "val"); + boolean var = is(forEach.elementVariable.type, scope, "var"); + if (!(val || var)) return false; + TypeBinding component = getForEachComponentType(forEach.collection, scope); if (component == null) return false; TypeReference replacement = makeType(component, forEach.elementVariable.type, false); - - forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal; + + if (val) forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal; forEach.elementVariable.annotations = addValAnnotation(forEach.elementVariable.annotations, forEach.elementVariable.type, scope); forEach.elementVariable.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(forEach.elementVariable.type, 3)); - + return false; } - + private static Annotation[] addValAnnotation(Annotation[] originals, TypeReference originalRef, BlockScope scope) { Annotation[] newAnn; if (originals != null) { @@ -219,12 +225,12 @@ public class PatchVal { } else { newAnn = new Annotation[1]; } - + newAnn[newAnn.length - 1] = new org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation(originalRef, originalRef.sourceStart); - + return newAnn; } - + private static TypeBinding getForEachComponentType(Expression collection, BlockScope scope) { if (collection != null) { TypeBinding resolved = collection.resolvedType; @@ -235,7 +241,7 @@ public class PatchVal { return resolved; } else if (resolved instanceof ReferenceBinding) { ReferenceBinding iterableType = ((ReferenceBinding)resolved).findSuperTypeOriginatingFrom(TypeIds.T_JavaLangIterable, false); - + TypeBinding[] arguments = null; if (iterableType != null) switch (iterableType.kind()) { case Binding.GENERIC_TYPE : // for (T t : Iterable) - in case used inside Iterable itself @@ -247,16 +253,16 @@ public class PatchVal { case Binding.RAW_TYPE : // for(Object e : Iterable) return null; } - + if (arguments != null && arguments.length == 1) { return arguments[0]; } } } - + return null; } - + private static TypeBinding resolveForExpression(Expression collection, BlockScope scope) { try { return collection.resolveType(scope); diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java index 7d5f36f4..ec1e8309 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java @@ -65,7 +65,9 @@ public class PatchValEclipse { ForeachStatement foreachDecl = (ForeachStatement) astStack[astPtr]; ASTNode init = foreachDecl.collection; if (init == null) return; - if (foreachDecl.elementVariable == null || !PatchVal.couldBeVal(foreachDecl.elementVariable.type)) return; + boolean val = PatchVal.couldBe("val", foreachDecl.elementVariable.type); + boolean var = PatchVal.couldBe("var", foreachDecl.elementVariable.type); + if (foreachDecl.elementVariable == null || !(val || var)) return; try { if (Reflection.iterableCopyField != null) Reflection.iterableCopyField.set(foreachDecl.elementVariable, init); @@ -88,7 +90,9 @@ public class PatchValEclipse { if (!(variableDecl instanceof LocalDeclaration)) return; ASTNode init = variableDecl.initialization; if (init == null) return; - if (!PatchVal.couldBeVal(variableDecl.type)) return; + boolean val = PatchVal.couldBe("val", variableDecl.type); + boolean var = PatchVal.couldBe("var", variableDecl.type); + if (!(val || var)) return; try { if (Reflection.initCopyField != null) Reflection.initCopyField.set(variableDecl, init); @@ -115,7 +119,7 @@ public class PatchValEclipse { Annotation valAnnotation = null; for (Annotation ann : in.annotations) { - if (PatchVal.couldBeVal(ann.type)) { + if (PatchVal.couldBe("val", ann.type)) { found = true; valAnnotation = ann; break; diff --git a/test/transform/resource/after-delombok/VarComplex.java b/test/transform/resource/after-delombok/VarComplex.java new file mode 100644 index 00000000..10b33943 --- /dev/null +++ b/test/transform/resource/after-delombok/VarComplex.java @@ -0,0 +1,20 @@ +public class VarComplex { + private String field = ""; + private static final int CONSTANT = 20; + public void testComplex() { + char[] shouldBeCharArray = field.toCharArray(); + int shouldBeInt = CONSTANT; + java.lang.Object lock = new Object(); + synchronized (lock) { + int field = 20; //Shadowing + int inner = 10; + switch (field) { + case 5: + char[] shouldBeCharArray2 = shouldBeCharArray; + int innerInner = inner; + + } + } + java.lang.String shouldBeString = field; //Unshadowing + } +} \ No newline at end of file diff --git a/test/transform/resource/after-delombok/VarInFor.java b/test/transform/resource/after-delombok/VarInFor.java new file mode 100644 index 00000000..363aeeff --- /dev/null +++ b/test/transform/resource/after-delombok/VarInFor.java @@ -0,0 +1,9 @@ +public class VarInFor { + public void enhancedFor() { + int[] list = new int[] {1, 2}; + for (int shouldBeInt : list) { + System.out.println(shouldBeInt); + int shouldBeInt2 = shouldBeInt; + } + } +} \ No newline at end of file diff --git a/test/transform/resource/after-delombok/VarInForOld.java b/test/transform/resource/after-delombok/VarInForOld.java new file mode 100644 index 00000000..bb510c0b --- /dev/null +++ b/test/transform/resource/after-delombok/VarInForOld.java @@ -0,0 +1,7 @@ +public class VarInForOld { + public void oldFor() { + for (int i = 0; i < 100; ++i) { + System.out.println(i); + } + } +} \ No newline at end of file diff --git a/test/transform/resource/after-delombok/VarModifier.java b/test/transform/resource/after-delombok/VarModifier.java new file mode 100644 index 00000000..9838cdf7 --- /dev/null +++ b/test/transform/resource/after-delombok/VarModifier.java @@ -0,0 +1,7 @@ +public class VarModifier { + private String field = ""; + public void testComplex() { + final char[] shouldBeFinalCharArray = field.toCharArray(); + char[] shouldBeCharArray = field.toCharArray(); + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/VarComplex.java b/test/transform/resource/after-ecj/VarComplex.java new file mode 100644 index 00000000..97a0a177 --- /dev/null +++ b/test/transform/resource/after-ecj/VarComplex.java @@ -0,0 +1,26 @@ +import lombok.var; +public class VarComplex { + private String field = ""; + private static final int CONSTANT = 20; + () { + } + public VarComplex() { + super(); + } + public void testComplex() { + @var char[] shouldBeCharArray = field.toCharArray(); + @var int shouldBeInt = CONSTANT; + @var java.lang.Object lock = new Object(); + synchronized (lock) + { + @var int field = 20; + @var int inner = 10; + switch (field) { + case 5 : + @var char[] shouldBeCharArray2 = shouldBeCharArray; + @var int innerInner = inner; + } + } + @var java.lang.String shouldBeString = field; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/VarInFor.java b/test/transform/resource/after-ecj/VarInFor.java new file mode 100644 index 00000000..1799d9b7 --- /dev/null +++ b/test/transform/resource/after-ecj/VarInFor.java @@ -0,0 +1,14 @@ +import lombok.var; +public class VarInFor { + public VarInFor() { + super(); + } + public void enhancedFor() { + int[] list = new int[]{1, 2}; + for (@var int shouldBeInt : list) + { + System.out.println(shouldBeInt); + @var int shouldBeInt2 = shouldBeInt; + } + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/VarInForOld.java b/test/transform/resource/after-ecj/VarInForOld.java new file mode 100644 index 00000000..065ea94d --- /dev/null +++ b/test/transform/resource/after-ecj/VarInForOld.java @@ -0,0 +1,12 @@ +import lombok.var; +public class VarInForOld { + public VarInForOld() { + super(); + } + public void oldFor() { + for (@var int i = 0;; (i < 100); ++ i) + { + System.out.println(i); + } + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/VarModifier.java b/test/transform/resource/after-ecj/VarModifier.java new file mode 100644 index 00000000..7f26534c --- /dev/null +++ b/test/transform/resource/after-ecj/VarModifier.java @@ -0,0 +1,12 @@ +import lombok.var; +public class VarModifier { + private String field = ""; + public VarModifier() { + super(); + } + public void testComplex() { + final @var char[] shouldBeFinalCharArray = field.toCharArray(); + @var char[] shouldBeCharArray = field.toCharArray(); + + } +} \ No newline at end of file diff --git a/test/transform/resource/before/VarComplex.java b/test/transform/resource/before/VarComplex.java new file mode 100644 index 00000000..c93e177a --- /dev/null +++ b/test/transform/resource/before/VarComplex.java @@ -0,0 +1,22 @@ +import lombok.var; + +public class VarComplex { + private String field = ""; + private static final int CONSTANT = 20; + + public void testComplex() { + var shouldBeCharArray = field.toCharArray(); + var shouldBeInt = CONSTANT; + var lock = new Object(); + synchronized (lock) { + var field = 20; //Shadowing + var inner = 10; + switch (field) { + case 5: + var shouldBeCharArray2 = shouldBeCharArray; + var innerInner = inner; + } + } + var shouldBeString = field; //Unshadowing + } +} \ No newline at end of file diff --git a/test/transform/resource/before/VarInFor.java b/test/transform/resource/before/VarInFor.java new file mode 100644 index 00000000..7f7bb7a7 --- /dev/null +++ b/test/transform/resource/before/VarInFor.java @@ -0,0 +1,11 @@ +import lombok.var; + +public class VarInFor { + public void enhancedFor() { + int[] list = new int[] {1, 2}; + for (var shouldBeInt : list) { + System.out.println(shouldBeInt); + var shouldBeInt2 = shouldBeInt; + } + } +} \ No newline at end of file diff --git a/test/transform/resource/before/VarInForOld.java b/test/transform/resource/before/VarInForOld.java new file mode 100644 index 00000000..99e83b57 --- /dev/null +++ b/test/transform/resource/before/VarInForOld.java @@ -0,0 +1,9 @@ +import lombok.var; + +public class VarInForOld { + public void oldFor() { + for (var i = 0; i < 100; ++i) { + System.out.println(i); + } + } +} \ No newline at end of file diff --git a/test/transform/resource/before/VarModifier.java b/test/transform/resource/before/VarModifier.java new file mode 100644 index 00000000..bb167fd2 --- /dev/null +++ b/test/transform/resource/before/VarModifier.java @@ -0,0 +1,10 @@ +import lombok.var; + +public class VarModifier { + private String field = ""; + + public void testComplex() { + final var shouldBeFinalCharArray = field.toCharArray(); + var shouldBeCharArray = field.toCharArray(); + } +} \ No newline at end of file -- cgit From 710a04607b2c8c875d1971dd517f9eace10a14b4 Mon Sep 17 00:00:00 2001 From: Bulgakov Alexander Date: Wed, 26 Oct 2016 23:18:47 +0300 Subject: The @var annotation has been moved to the experimental package. Added a test of a @var variable with null initialization. --- src/core/lombok/eclipse/handlers/HandleVal.java | 2 +- src/core/lombok/experimental/var.java | 28 ++++++++++ src/core/lombok/javac/handlers/HandleVal.java | 6 +-- src/core/lombok/var.java | 28 ---------- .../lombok/eclipse/agent/PatchVal.java | 60 ++++++++++++---------- .../lombok/eclipse/agent/PatchValEclipse.java | 25 +++++---- .../resource/after-delombok/ValNullInit.java | 5 ++ .../resource/after-delombok/VarNullInit.java | 5 ++ test/transform/resource/after-ecj/ValNullInit.java | 10 ++++ test/transform/resource/after-ecj/VarComplex.java | 2 +- test/transform/resource/after-ecj/VarInFor.java | 2 +- test/transform/resource/after-ecj/VarInForOld.java | 2 +- test/transform/resource/after-ecj/VarModifier.java | 2 +- test/transform/resource/after-ecj/VarNullInit.java | 10 ++++ test/transform/resource/before/ValNullInit.java | 7 +++ test/transform/resource/before/VarComplex.java | 2 +- test/transform/resource/before/VarInFor.java | 2 +- test/transform/resource/before/VarInForOld.java | 2 +- test/transform/resource/before/VarModifier.java | 2 +- test/transform/resource/before/VarNullInit.java | 7 +++ 20 files changed, 132 insertions(+), 77 deletions(-) create mode 100644 src/core/lombok/experimental/var.java delete mode 100644 src/core/lombok/var.java create mode 100644 test/transform/resource/after-delombok/ValNullInit.java create mode 100644 test/transform/resource/after-delombok/VarNullInit.java create mode 100644 test/transform/resource/after-ecj/ValNullInit.java create mode 100644 test/transform/resource/after-ecj/VarNullInit.java create mode 100644 test/transform/resource/before/ValNullInit.java create mode 100644 test/transform/resource/before/VarNullInit.java diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java index 1f54bfac..0a136d0b 100644 --- a/src/core/lombok/eclipse/handlers/HandleVal.java +++ b/src/core/lombok/eclipse/handlers/HandleVal.java @@ -32,7 +32,7 @@ import lombok.eclipse.EclipseASTAdapter; import lombok.eclipse.EclipseASTVisitor; import lombok.eclipse.EclipseNode; -import lombok.var; +import lombok.experimental.var; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.ForStatement; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; diff --git a/src/core/lombok/experimental/var.java b/src/core/lombok/experimental/var.java new file mode 100644 index 00000000..d8de8b19 --- /dev/null +++ b/src/core/lombok/experimental/var.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010-2013 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.experimental; + +/** + * like val but not final + */ +public @interface var { +} diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index f4c816d6..5b90ff62 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -31,7 +31,7 @@ import lombok.ConfigurationKeys; import lombok.core.HandlerPriority; import lombok.javac.*; import lombok.val; -import lombok.var; +import lombok.experimental.var; import org.mangosdk.spi.ProviderFor; import static lombok.core.handlers.HandlerUtil.handleFlagUsage; @@ -88,8 +88,8 @@ public class HandleVal extends JavacASTAdapter { } if (localNode.shouldDeleteLombokAnnotations()) { - JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, "lombok.val"); - JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, "lombok.var"); + JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, val.class.getName()); + JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, var.class.getName()); } if (isVal) local.mods.flags |= Flags.FINAL; diff --git a/src/core/lombok/var.java b/src/core/lombok/var.java deleted file mode 100644 index 397f2760..00000000 --- a/src/core/lombok/var.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2010-2013 The Project Lombok Authors. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package lombok; - -/** - * like val but not final - */ -public @interface var { -} diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index 4a43c873..43e93bbe 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -21,22 +21,9 @@ */ package lombok.eclipse.agent; -import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.eclipse.jdt.internal.compiler.ast.Expression; -import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; -import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; -import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; -import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; -import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; -import org.eclipse.jdt.internal.compiler.lookup.Binding; -import org.eclipse.jdt.internal.compiler.lookup.BlockScope; -import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; -import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; -import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; -import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; -import org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import org.eclipse.jdt.internal.compiler.lookup.*; import java.lang.reflect.Field; @@ -85,15 +72,21 @@ public class PatchVal { } public static boolean couldBe(String key, TypeReference ref) { + String[] keyParts = key.split("\\."); if (ref instanceof SingleTypeReference) { char[] token = ((SingleTypeReference)ref).token; - return matches(key, token); + return matches(keyParts[keyParts.length - 1], token); } if (ref instanceof QualifiedTypeReference) { char[][] tokens = ((QualifiedTypeReference)ref).tokens; - if (tokens == null || tokens.length != 2) return false; - return matches("lombok", tokens[0]) && matches(key, tokens[1]); + if (keyParts.length != tokens.length) return false; + for(int i = 0; i < tokens.length; ++i) { + String part = keyParts[i]; + char[] token = tokens[i]; + if (!matches(part, token)) return false; + } + return true; } return false; @@ -105,10 +98,17 @@ public class PatchVal { TypeBinding resolvedType = ref.resolvedType; if (resolvedType == null) resolvedType = ref.resolveType(scope, false); if (resolvedType == null) return false; - + char[] pkg = resolvedType.qualifiedPackageName(); char[] nm = resolvedType.qualifiedSourceName(); - return matches("lombok", pkg) && matches(key, nm); + int pkgFullLength = pkg.length > 0 ? pkg.length + 1: 0; + char[] fullName = new char[pkgFullLength + nm.length]; + if(pkg.length > 0) { + System.arraycopy(pkg, 0, fullName, 0, pkg.length); + fullName[pkg.length] = '.'; + } + System.arraycopy(nm, 0, fullName, pkgFullLength, nm.length); + return matches(key, fullName); } public static final class Reflection { @@ -132,8 +132,8 @@ public class PatchVal { if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false; boolean decomponent = false; - boolean val = is(local.type, scope, "val"); - boolean var = is(local.type, scope, "var"); + boolean val = isVal(local, scope); + boolean var = isVar(local, scope); if (!(val || var)) return false; StackTraceElement[] st = new Throwable().getStackTrace(); @@ -198,11 +198,19 @@ public class PatchVal { return false; } + private static boolean isVar(LocalDeclaration local, BlockScope scope) { + return is(local.type, scope, "lombok.experimental.var"); + } + + private static boolean isVal(LocalDeclaration local, BlockScope scope) { + return is(local.type, scope, "lombok.val"); + } + public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) { if (forEach.elementVariable == null) return false; - - boolean val = is(forEach.elementVariable.type, scope, "val"); - boolean var = is(forEach.elementVariable.type, scope, "var"); + + boolean val = isVal(forEach.elementVariable, scope); + boolean var = isVar(forEach.elementVariable, scope); if (!(val || var)) return false; TypeBinding component = getForEachComponentType(forEach.collection, scope); @@ -216,7 +224,7 @@ public class PatchVal { return false; } - + private static Annotation[] addValAnnotation(Annotation[] originals, TypeReference originalRef, BlockScope scope) { Annotation[] newAnn; if (originals != null) { diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java index ec1e8309..d0c29ac8 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java @@ -41,12 +41,7 @@ import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; -import org.eclipse.jdt.internal.compiler.ast.ASTNode; -import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; -import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; -import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; -import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.parser.Parser; @@ -65,8 +60,8 @@ public class PatchValEclipse { ForeachStatement foreachDecl = (ForeachStatement) astStack[astPtr]; ASTNode init = foreachDecl.collection; if (init == null) return; - boolean val = PatchVal.couldBe("val", foreachDecl.elementVariable.type); - boolean var = PatchVal.couldBe("var", foreachDecl.elementVariable.type); + boolean val = couldBeVal(foreachDecl.elementVariable.type); + boolean var = couldBeVar(foreachDecl.elementVariable.type); if (foreachDecl.elementVariable == null || !(val || var)) return; try { @@ -90,8 +85,8 @@ public class PatchValEclipse { if (!(variableDecl instanceof LocalDeclaration)) return; ASTNode init = variableDecl.initialization; if (init == null) return; - boolean val = PatchVal.couldBe("val", variableDecl.type); - boolean var = PatchVal.couldBe("var", variableDecl.type); + boolean val = couldBeVal(variableDecl.type); + boolean var = couldBeVar(variableDecl.type); if (!(val || var)) return; try { @@ -101,6 +96,10 @@ public class PatchValEclipse { } } + private static boolean couldBeVar(TypeReference type) { + return PatchVal.couldBe("lombok.experimental.var", type); + } + public static void addFinalAndValAnnotationToSingleVariableDeclaration(Object converter, SingleVariableDeclaration out, LocalDeclaration in) { @SuppressWarnings("unchecked") List modifiers = out.modifiers(); addFinalAndValAnnotationToModifierList(converter, modifiers, out.getAST(), in); @@ -119,7 +118,7 @@ public class PatchValEclipse { Annotation valAnnotation = null; for (Annotation ann : in.annotations) { - if (PatchVal.couldBe("val", ann.type)) { + if (couldBeVal(ann.type)) { found = true; valAnnotation = ann; break; @@ -171,6 +170,10 @@ public class PatchValEclipse { } } + private static boolean couldBeVal(TypeReference type) { + return PatchVal.couldBe("lombok.val", type); + } + public static Modifier createModifier(AST ast, ModifierKeyword keyword, int start, int end) { Modifier modifier = null; try { diff --git a/test/transform/resource/after-delombok/ValNullInit.java b/test/transform/resource/after-delombok/ValNullInit.java new file mode 100644 index 00000000..f0834a63 --- /dev/null +++ b/test/transform/resource/after-delombok/ValNullInit.java @@ -0,0 +1,5 @@ +class ValNullInit { + void method() { + final java.lang.Object x = null; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-delombok/VarNullInit.java b/test/transform/resource/after-delombok/VarNullInit.java new file mode 100644 index 00000000..f0087765 --- /dev/null +++ b/test/transform/resource/after-delombok/VarNullInit.java @@ -0,0 +1,5 @@ +class VarNullInit { + void method() { + java.lang.Object x = null; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/ValNullInit.java b/test/transform/resource/after-ecj/ValNullInit.java new file mode 100644 index 00000000..bd89c023 --- /dev/null +++ b/test/transform/resource/after-ecj/ValNullInit.java @@ -0,0 +1,10 @@ +import lombok.val; + +class ValNullInit { + ValNullInit() { + super(); + } + void method() { + final @val java.lang.Object x = null; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/VarComplex.java b/test/transform/resource/after-ecj/VarComplex.java index 97a0a177..10c456eb 100644 --- a/test/transform/resource/after-ecj/VarComplex.java +++ b/test/transform/resource/after-ecj/VarComplex.java @@ -1,4 +1,4 @@ -import lombok.var; +import lombok.experimental.var; public class VarComplex { private String field = ""; private static final int CONSTANT = 20; diff --git a/test/transform/resource/after-ecj/VarInFor.java b/test/transform/resource/after-ecj/VarInFor.java index 1799d9b7..0192aaed 100644 --- a/test/transform/resource/after-ecj/VarInFor.java +++ b/test/transform/resource/after-ecj/VarInFor.java @@ -1,4 +1,4 @@ -import lombok.var; +import lombok.experimental.var; public class VarInFor { public VarInFor() { super(); diff --git a/test/transform/resource/after-ecj/VarInForOld.java b/test/transform/resource/after-ecj/VarInForOld.java index 065ea94d..98fedf03 100644 --- a/test/transform/resource/after-ecj/VarInForOld.java +++ b/test/transform/resource/after-ecj/VarInForOld.java @@ -1,4 +1,4 @@ -import lombok.var; +import lombok.experimental.var; public class VarInForOld { public VarInForOld() { super(); diff --git a/test/transform/resource/after-ecj/VarModifier.java b/test/transform/resource/after-ecj/VarModifier.java index 7f26534c..d11142ca 100644 --- a/test/transform/resource/after-ecj/VarModifier.java +++ b/test/transform/resource/after-ecj/VarModifier.java @@ -1,4 +1,4 @@ -import lombok.var; +import lombok.experimental.var; public class VarModifier { private String field = ""; public VarModifier() { diff --git a/test/transform/resource/after-ecj/VarNullInit.java b/test/transform/resource/after-ecj/VarNullInit.java new file mode 100644 index 00000000..966afdc9 --- /dev/null +++ b/test/transform/resource/after-ecj/VarNullInit.java @@ -0,0 +1,10 @@ +import lombok.experimental.var; + +class VarNullInit { + ValNullInit() { + super(); + } + void method() { + final @var java.lang.Object x = null; + } +} \ No newline at end of file diff --git a/test/transform/resource/before/ValNullInit.java b/test/transform/resource/before/ValNullInit.java new file mode 100644 index 00000000..649bc0cd --- /dev/null +++ b/test/transform/resource/before/ValNullInit.java @@ -0,0 +1,7 @@ +import lombok.val; + +class ValNullInit { + void method() { + val x = null; + } +} \ No newline at end of file diff --git a/test/transform/resource/before/VarComplex.java b/test/transform/resource/before/VarComplex.java index c93e177a..a30492ac 100644 --- a/test/transform/resource/before/VarComplex.java +++ b/test/transform/resource/before/VarComplex.java @@ -1,4 +1,4 @@ -import lombok.var; +import lombok.experimental.var; public class VarComplex { private String field = ""; diff --git a/test/transform/resource/before/VarInFor.java b/test/transform/resource/before/VarInFor.java index 7f7bb7a7..ed22edc3 100644 --- a/test/transform/resource/before/VarInFor.java +++ b/test/transform/resource/before/VarInFor.java @@ -1,4 +1,4 @@ -import lombok.var; +import lombok.experimental.var; public class VarInFor { public void enhancedFor() { diff --git a/test/transform/resource/before/VarInForOld.java b/test/transform/resource/before/VarInForOld.java index 99e83b57..3000f58c 100644 --- a/test/transform/resource/before/VarInForOld.java +++ b/test/transform/resource/before/VarInForOld.java @@ -1,4 +1,4 @@ -import lombok.var; +import lombok.experimental.var; public class VarInForOld { public void oldFor() { diff --git a/test/transform/resource/before/VarModifier.java b/test/transform/resource/before/VarModifier.java index bb167fd2..5c68caa7 100644 --- a/test/transform/resource/before/VarModifier.java +++ b/test/transform/resource/before/VarModifier.java @@ -1,4 +1,4 @@ -import lombok.var; +import lombok.experimental.var; public class VarModifier { private String field = ""; diff --git a/test/transform/resource/before/VarNullInit.java b/test/transform/resource/before/VarNullInit.java new file mode 100644 index 00000000..14d7b4a3 --- /dev/null +++ b/test/transform/resource/before/VarNullInit.java @@ -0,0 +1,7 @@ +import lombok.experimental.var; + +class VarNullInit { + void method() { + var x = null; + } +} \ No newline at end of file -- cgit From 62672c03855f6035a01c2b84fe783be0f6231899 Mon Sep 17 00:00:00 2001 From: Bulgakov Alexander Date: Thu, 27 Oct 2016 23:52:50 +0300 Subject: Use of the 'var' feature is disabled by default. Include lombok.var.flagUsage = ALLOW in lombok.config to enable var --- .../lombok/core/configuration/AllowHelper.java | 37 ++++++++++ .../lombok/core/configuration/FlagUsageType.java | 2 +- src/core/lombok/core/handlers/HandlerUtil.java | 86 +++++++++++----------- test/transform/resource/after-ecj/VarNullInit.java | 4 +- test/transform/resource/before/VarComplex.java | 1 + test/transform/resource/before/VarInFor.java | 1 + test/transform/resource/before/VarInForOld.java | 1 + test/transform/resource/before/VarModifier.java | 1 + test/transform/resource/before/VarNullInit.java | 1 + 9 files changed, 87 insertions(+), 47 deletions(-) create mode 100644 src/core/lombok/core/configuration/AllowHelper.java diff --git a/src/core/lombok/core/configuration/AllowHelper.java b/src/core/lombok/core/configuration/AllowHelper.java new file mode 100644 index 00000000..31d09381 --- /dev/null +++ b/src/core/lombok/core/configuration/AllowHelper.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.core.configuration; + +import java.util.HashSet; +import java.util.Set; + +import static java.util.Arrays.asList; + +public class AllowHelper { + private final static Set allowable = new HashSet(asList( + "var" + )); + + public static boolean isAllowable(String feature) { + return allowable.contains(feature); + } +} diff --git a/src/core/lombok/core/configuration/FlagUsageType.java b/src/core/lombok/core/configuration/FlagUsageType.java index b7053b7c..8717c22b 100644 --- a/src/core/lombok/core/configuration/FlagUsageType.java +++ b/src/core/lombok/core/configuration/FlagUsageType.java @@ -23,5 +23,5 @@ package lombok.core.configuration; /** Used for lombok configuration to flag usages of certain lombok feature. */ public enum FlagUsageType { - WARNING, ERROR; + WARNING, ERROR, ALLOW; } diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index a9a4be78..cdca991a 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -21,34 +21,25 @@ */ package lombok.core.handlers; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; - -import lombok.AllArgsConstructor; -import lombok.ConfigurationKeys; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.ToString; -import lombok.Value; +import lombok.*; import lombok.core.AST; import lombok.core.AnnotationValues; import lombok.core.JavaIdentifiers; import lombok.core.LombokNode; +import lombok.core.configuration.AllowHelper; import lombok.core.configuration.ConfigurationKey; import lombok.core.configuration.FlagUsageType; import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; import lombok.experimental.Wither; +import java.util.*; +import java.util.regex.Pattern; + +import static lombok.core.configuration.FlagUsageType.ALLOW; +import static lombok.core.configuration.FlagUsageType.ERROR; +import static lombok.core.configuration.FlagUsageType.WARNING; + /** * Container for static utility methods useful for some of the standard lombok handlers, regardless of * target platform (e.g. useful for both javac and Eclipse lombok implementations). @@ -73,7 +64,7 @@ public class HandlerUtil { } /** Checks if the given name is a valid identifier. - * + * * If it is, this returns {@code true} and does nothing else. * If it isn't, this returns {@code false} and adds an error message to the supplied node. */ @@ -95,12 +86,19 @@ public class HandlerUtil { return Singulars.autoSingularize(plural); } public static void handleFlagUsage(LombokNode node, ConfigurationKey key, String featureName) { + boolean allowable = AllowHelper.isAllowable(featureName); + FlagUsageType fut = node.getAst().readConfiguration(key); + boolean allowed = !allowable || ALLOW == fut; + if (!allowed) { + node.addError("Use of " + featureName + " is disabled by default. Please use flag " + ALLOW + " to enable."); + } + if (fut != null) { String msg = "Use of " + featureName + " is flagged according to lombok configuration."; - if (fut == FlagUsageType.WARNING) node.addWarning(msg); - else node.addError(msg); + if (fut == WARNING) node.addWarning(msg); + else if (fut == ERROR) node.addError(msg); } } @@ -114,13 +112,13 @@ public class HandlerUtil { FlagUsageType fut = null; String featureName = null; - if (fut1 == FlagUsageType.ERROR) { + if (fut1 == ERROR) { fut = fut1; featureName = featureName1; - } else if (fut2 == FlagUsageType.ERROR) { + } else if (fut2 == ERROR) { fut = fut2; featureName = featureName2; - } else if (fut1 == FlagUsageType.WARNING) { + } else if (fut1 == WARNING) { fut = fut1; featureName = featureName1; } else { @@ -130,7 +128,7 @@ public class HandlerUtil { if (fut != null) { String msg = "Use of " + featureName + " is flagged according to lombok configuration."; - if (fut == FlagUsageType.WARNING) node.addWarning(msg); + if (fut == WARNING) node.addWarning(msg); else node.addError(msg); } } @@ -161,8 +159,8 @@ public class HandlerUtil { @SuppressWarnings({"all", "unchecked", "deprecation"}) public static final List> INVALID_ON_BUILDERS = Collections.unmodifiableList( Arrays.>asList( - Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class, - RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class, + Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class, + RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class, Data.class, Value.class, lombok.experimental.Value.class, FieldDefaults.class)); /** @@ -170,9 +168,9 @@ public class HandlerUtil { * For prefixes that end in a letter character, the next character must be a non-lowercase character (i.e. {@code hashCode} is not {@code ashCode} even if * {@code h} is in the prefix list, but {@code hAshcode} would become {@code ashCode}). The first prefix that matches is used. If the prefix list is empty, * or the empty string is in the prefix list and no prefix before it matches, the fieldName will be returned verbatim. - * + * * If no prefix matches and the empty string is not in the prefix list and the prefix list is not empty, {@code null} is returned. - * + * * @param fieldName The full name of a field. * @param prefixes A list of prefixes, usually provided by the {@code Accessors} settings annotation, listing field prefixes. * @return The base name of the field. @@ -215,19 +213,19 @@ public class HandlerUtil { /** * Generates a getter name from a given field name. - * + * * Strategy: *
    *
  • Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit * the prefix list, this method immediately returns {@code null}.
  • *
  • If {@code Accessors} has {@code fluent=true}, then return the basename.
  • *
  • Pick a prefix. 'get' normally, but 'is' if {@code isBoolean} is true.
  • - *
  • Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. If so, return the field name verbatim.
  • + *
  • Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. If so, return the field name verbatim.
  • *
  • Check if the first character of the field is lowercase. If so, check if the second character * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.
  • *
  • Return the prefix plus the possibly title/uppercased first character, and the rest of the field name.
  • *
- * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. @@ -239,19 +237,19 @@ public class HandlerUtil { /** * Generates a setter name from a given field name. - * + * * Strategy: *
    *
  • Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit * the prefix list, this method immediately returns {@code null}.
  • *
  • If {@code Accessors} has {@code fluent=true}, then return the basename.
  • *
  • Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. - * If so, replace {@code is} with {@code set} and return that.
  • + * If so, replace {@code is} with {@code set} and return that. *
  • Check if the first character of the field is lowercase. If so, check if the second character * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.
  • *
  • Return {@code "set"} plus the possibly title/uppercased first character, and the rest of the field name.
  • *
- * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. @@ -263,18 +261,18 @@ public class HandlerUtil { /** * Generates a wither name from a given field name. - * + * * Strategy: *
    *
  • Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit * the prefix list, this method immediately returns {@code null}.
  • *
  • Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. - * If so, replace {@code is} with {@code with} and return that.
  • + * If so, replace {@code is} with {@code with} and return that. *
  • Check if the first character of the field is lowercase. If so, check if the second character * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.
  • *
  • Return {@code "with"} plus the possibly title/uppercased first character, and the rest of the field name.
  • *
- * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. @@ -315,10 +313,10 @@ public class HandlerUtil { /** * Returns all names of methods that would represent the getter for a field with the provided name. - * + * * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:
* {@code [isRunning, getRunning, isIsRunning, getIsRunning]} - * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. @@ -329,10 +327,10 @@ public class HandlerUtil { /** * Returns all names of methods that would represent the setter for a field with the provided name. - * + * * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:
* {@code [setRunning, setIsRunning]} - * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. @@ -343,10 +341,10 @@ public class HandlerUtil { /** * Returns all names of methods that would represent the wither for a field with the provided name. - * + * * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:
* {@code [withRunning, withIsRunning]} - * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. diff --git a/test/transform/resource/after-ecj/VarNullInit.java b/test/transform/resource/after-ecj/VarNullInit.java index 966afdc9..ba8c3e46 100644 --- a/test/transform/resource/after-ecj/VarNullInit.java +++ b/test/transform/resource/after-ecj/VarNullInit.java @@ -1,10 +1,10 @@ import lombok.experimental.var; class VarNullInit { - ValNullInit() { + VarNullInit() { super(); } void method() { - final @var java.lang.Object x = null; + @var java.lang.Object x = null; } } \ No newline at end of file diff --git a/test/transform/resource/before/VarComplex.java b/test/transform/resource/before/VarComplex.java index a30492ac..bfaa8804 100644 --- a/test/transform/resource/before/VarComplex.java +++ b/test/transform/resource/before/VarComplex.java @@ -1,3 +1,4 @@ +//CONF: lombok.var.flagUsage = ALLOW import lombok.experimental.var; public class VarComplex { diff --git a/test/transform/resource/before/VarInFor.java b/test/transform/resource/before/VarInFor.java index ed22edc3..cc8c387e 100644 --- a/test/transform/resource/before/VarInFor.java +++ b/test/transform/resource/before/VarInFor.java @@ -1,3 +1,4 @@ +//CONF: lombok.var.flagUsage = ALLOW import lombok.experimental.var; public class VarInFor { diff --git a/test/transform/resource/before/VarInForOld.java b/test/transform/resource/before/VarInForOld.java index 3000f58c..f90aba7f 100644 --- a/test/transform/resource/before/VarInForOld.java +++ b/test/transform/resource/before/VarInForOld.java @@ -1,3 +1,4 @@ +//CONF: lombok.var.flagUsage = ALLOW import lombok.experimental.var; public class VarInForOld { diff --git a/test/transform/resource/before/VarModifier.java b/test/transform/resource/before/VarModifier.java index 5c68caa7..7250c1c5 100644 --- a/test/transform/resource/before/VarModifier.java +++ b/test/transform/resource/before/VarModifier.java @@ -1,3 +1,4 @@ +//CONF: lombok.var.flagUsage = ALLOW import lombok.experimental.var; public class VarModifier { diff --git a/test/transform/resource/before/VarNullInit.java b/test/transform/resource/before/VarNullInit.java index 14d7b4a3..f7f9c55b 100644 --- a/test/transform/resource/before/VarNullInit.java +++ b/test/transform/resource/before/VarNullInit.java @@ -1,3 +1,4 @@ +//CONF: lombok.var.flagUsage = ALLOW import lombok.experimental.var; class VarNullInit { -- cgit From 4fbbd8ed03f7963715bc6e7c7de6faae0b1ba917 Mon Sep 17 00:00:00 2001 From: Bulgakov Alexander Date: Mon, 31 Oct 2016 23:52:20 +0300 Subject: added var.html --- buildScripts/website.ant.xml | 3 + usage_examples/experimental/varExample_post.jpage | 21 ++++++ usage_examples/experimental/varExample_pre.jpage | 21 ++++++ website/features/experimental/index.html | 2 + website/features/experimental/var.html | 83 +++++++++++++++++++++++ 5 files changed, 130 insertions(+) create mode 100644 usage_examples/experimental/varExample_post.jpage create mode 100644 usage_examples/experimental/varExample_pre.jpage create mode 100644 website/features/experimental/var.html diff --git a/buildScripts/website.ant.xml b/buildScripts/website.ant.xml index aae2285f..19b995d3 100644 --- a/buildScripts/website.ant.xml +++ b/buildScripts/website.ant.xml @@ -154,6 +154,9 @@ such as converting the changelog into HTML, and creating javadoc. + + + diff --git a/usage_examples/experimental/varExample_post.jpage b/usage_examples/experimental/varExample_post.jpage new file mode 100644 index 00000000..25623510 --- /dev/null +++ b/usage_examples/experimental/varExample_post.jpage @@ -0,0 +1,21 @@ +import java.util.ArrayList; +import lombok.var; + +public class ValExample { + public String example() { + ArrayList example = new ArrayList(); + example.add("Hello, World!"); + final String foo = example.get(0); + return foo.toLowerCase(); + } + + public void example2() { + ArrayList list = new ArrayList(); + list.add("zero"); + list.add("one"); + list.add("two"); + for(int i = 0; i < list.size(); ++i) { + System.out.printf("%d: %s\n", i, list.get(i)); + } + } +} diff --git a/usage_examples/experimental/varExample_pre.jpage b/usage_examples/experimental/varExample_pre.jpage new file mode 100644 index 00000000..0786c0df --- /dev/null +++ b/usage_examples/experimental/varExample_pre.jpage @@ -0,0 +1,21 @@ +import java.util.ArrayList; +import lombok.var; + +public class ValExample { + public String example() { + var example = new ArrayList(); + example.add("Hello, World!"); + final var foo = example.get(0); + return foo.toLowerCase(); + } + + public void example2() { + var list = new ArrayList(); + list.add("zero"); + list.add("one"); + list.add("two"); + for(var i = 0; i < list.size(); ++i) { + System.out.printf("%d: %s\n", i, list.get(i)); + } + } +} diff --git a/website/features/experimental/index.html b/website/features/experimental/index.html index f3893674..4a6d12bb 100644 --- a/website/features/experimental/index.html +++ b/website/features/experimental/index.html @@ -22,6 +22,8 @@ Features that receive positive community feedback and which seem to produce clean, flexible code will eventually become accepted as a core feature and move out of the experimental package.
+
@var
+
The same as @val but modifiable.
@Accessors
A more fluent API for getters and setters.
@ExtensionMethod
diff --git a/website/features/experimental/var.html b/website/features/experimental/var.html new file mode 100644 index 00000000..7a045c00 --- /dev/null +++ b/website/features/experimental/var.html @@ -0,0 +1,83 @@ + + + + + + + + EXPERIMENTAL – @var +
+
+
+ +

@var

+ +
+

Since

+

+ @var was introduced as experimental feature in lombok v1.16.11. +

+
+
+

Experimental

+

+ Experimental because: +

    +
  • This feature is very controversial.
  • +
  • There is JEP 286 that should make the @var obsolete.
  • +
Current status: uncertain - Currently we feel this feature cannot move out of experimental status. +
+
+

Overview

+

+ The @var has the same mission as the @val annotation but modifable. +

+
+
+
+

With Lombok

+
@HTML_PRE@
+
+
+
+

Vanilla Java

+
@HTML_POST@
+
+
+
+
+

Supported configuration keys:

+
+
lombok.val.flagUsage = [allow] (default: not set)
+
Lombok will flag any usage of var as a error if not configured.
+
+
+
+

Small print

+

+ For compound types, the most common superclass is inferred, not any shared interfaces. For example, bool ? new HashSet() : new ArrayList() + is an expression with a compound type: The result is both AbstractCollection as well as Serializable. The type inferred will be + AbstractCollection, as that is a class, whereas Serializable is an interface. +

+ In ambiguous cases, such as when the initializer expression is null, java.lang.Object is inferred. +

+
+
+ +
+
+
+ + + -- cgit From 67371e5841e1dd8ed5d663f1c907da0952976b8f Mon Sep 17 00:00:00 2001 From: Bulgakov Alexander Date: Sat, 12 Nov 2016 15:56:17 +0300 Subject: imports expanded, idents aligned --- src/core/lombok/core/handlers/HandlerUtil.java | 82 ++++++++++++---------- src/core/lombok/eclipse/handlers/HandleVal.java | 25 ++++--- src/core/lombok/javac/handlers/HandleVal.java | 41 ++++++----- .../lombok/eclipse/agent/PatchVal.java | 71 +++++++++++-------- .../lombok/eclipse/agent/PatchValEclipse.java | 8 ++- 5 files changed, 132 insertions(+), 95 deletions(-) diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index cdca991a..a321e67f 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -21,7 +21,24 @@ */ package lombok.core.handlers; -import lombok.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import lombok.AllArgsConstructor; +import lombok.ConfigurationKeys; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.Value; import lombok.core.AST; import lombok.core.AnnotationValues; import lombok.core.JavaIdentifiers; @@ -33,13 +50,6 @@ import lombok.experimental.Accessors; import lombok.experimental.FieldDefaults; import lombok.experimental.Wither; -import java.util.*; -import java.util.regex.Pattern; - -import static lombok.core.configuration.FlagUsageType.ALLOW; -import static lombok.core.configuration.FlagUsageType.ERROR; -import static lombok.core.configuration.FlagUsageType.WARNING; - /** * Container for static utility methods useful for some of the standard lombok handlers, regardless of * target platform (e.g. useful for both javac and Eclipse lombok implementations). @@ -64,7 +74,7 @@ public class HandlerUtil { } /** Checks if the given name is a valid identifier. - * + * * If it is, this returns {@code true} and does nothing else. * If it isn't, this returns {@code false} and adds an error message to the supplied node. */ @@ -90,15 +100,15 @@ public class HandlerUtil { FlagUsageType fut = node.getAst().readConfiguration(key); - boolean allowed = !allowable || ALLOW == fut; + boolean allowed = !allowable || FlagUsageType.ALLOW == fut; if (!allowed) { - node.addError("Use of " + featureName + " is disabled by default. Please use flag " + ALLOW + " to enable."); + node.addError("Use of " + featureName + " is disabled by default. Please use flag " + FlagUsageType.ALLOW + " to enable."); } if (fut != null) { String msg = "Use of " + featureName + " is flagged according to lombok configuration."; - if (fut == WARNING) node.addWarning(msg); - else if (fut == ERROR) node.addError(msg); + if (fut == FlagUsageType.WARNING) node.addWarning(msg); + else if (fut == FlagUsageType.ERROR) node.addError(msg); } } @@ -112,13 +122,13 @@ public class HandlerUtil { FlagUsageType fut = null; String featureName = null; - if (fut1 == ERROR) { + if (fut1 == FlagUsageType.ERROR) { fut = fut1; featureName = featureName1; - } else if (fut2 == ERROR) { + } else if (fut2 == FlagUsageType.ERROR) { fut = fut2; featureName = featureName2; - } else if (fut1 == WARNING) { + } else if (fut1 == FlagUsageType.WARNING) { fut = fut1; featureName = featureName1; } else { @@ -128,7 +138,7 @@ public class HandlerUtil { if (fut != null) { String msg = "Use of " + featureName + " is flagged according to lombok configuration."; - if (fut == WARNING) node.addWarning(msg); + if (fut == FlagUsageType.WARNING) node.addWarning(msg); else node.addError(msg); } } @@ -159,8 +169,8 @@ public class HandlerUtil { @SuppressWarnings({"all", "unchecked", "deprecation"}) public static final List> INVALID_ON_BUILDERS = Collections.unmodifiableList( Arrays.>asList( - Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class, - RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class, + Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class, + RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class, Data.class, Value.class, lombok.experimental.Value.class, FieldDefaults.class)); /** @@ -168,9 +178,9 @@ public class HandlerUtil { * For prefixes that end in a letter character, the next character must be a non-lowercase character (i.e. {@code hashCode} is not {@code ashCode} even if * {@code h} is in the prefix list, but {@code hAshcode} would become {@code ashCode}). The first prefix that matches is used. If the prefix list is empty, * or the empty string is in the prefix list and no prefix before it matches, the fieldName will be returned verbatim. - * + * * If no prefix matches and the empty string is not in the prefix list and the prefix list is not empty, {@code null} is returned. - * + * * @param fieldName The full name of a field. * @param prefixes A list of prefixes, usually provided by the {@code Accessors} settings annotation, listing field prefixes. * @return The base name of the field. @@ -213,19 +223,19 @@ public class HandlerUtil { /** * Generates a getter name from a given field name. - * + * * Strategy: *
    *
  • Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit * the prefix list, this method immediately returns {@code null}.
  • *
  • If {@code Accessors} has {@code fluent=true}, then return the basename.
  • *
  • Pick a prefix. 'get' normally, but 'is' if {@code isBoolean} is true.
  • - *
  • Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. If so, return the field name verbatim.
  • + *
  • Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. If so, return the field name verbatim.
  • *
  • Check if the first character of the field is lowercase. If so, check if the second character * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.
  • *
  • Return the prefix plus the possibly title/uppercased first character, and the rest of the field name.
  • *
- * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. @@ -237,19 +247,19 @@ public class HandlerUtil { /** * Generates a setter name from a given field name. - * + * * Strategy: *
    *
  • Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit * the prefix list, this method immediately returns {@code null}.
  • *
  • If {@code Accessors} has {@code fluent=true}, then return the basename.
  • *
  • Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. - * If so, replace {@code is} with {@code set} and return that.
  • + * If so, replace {@code is} with {@code set} and return that. *
  • Check if the first character of the field is lowercase. If so, check if the second character * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.
  • *
  • Return {@code "set"} plus the possibly title/uppercased first character, and the rest of the field name.
  • *
- * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. @@ -261,18 +271,18 @@ public class HandlerUtil { /** * Generates a wither name from a given field name. - * + * * Strategy: *
    *
  • Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit * the prefix list, this method immediately returns {@code null}.
  • *
  • Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. - * If so, replace {@code is} with {@code with} and return that.
  • + * If so, replace {@code is} with {@code with} and return that. *
  • Check if the first character of the field is lowercase. If so, check if the second character * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.
  • *
  • Return {@code "with"} plus the possibly title/uppercased first character, and the rest of the field name.
  • *
- * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. @@ -313,10 +323,10 @@ public class HandlerUtil { /** * Returns all names of methods that would represent the getter for a field with the provided name. - * + * * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:
* {@code [isRunning, getRunning, isIsRunning, getIsRunning]} - * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. @@ -327,10 +337,10 @@ public class HandlerUtil { /** * Returns all names of methods that would represent the setter for a field with the provided name. - * + * * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:
* {@code [setRunning, setIsRunning]} - * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. @@ -341,10 +351,10 @@ public class HandlerUtil { /** * Returns all names of methods that would represent the wither for a field with the provided name. - * + * * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:
* {@code [withRunning, withIsRunning]} - * + * * @param accessors Accessors configuration. * @param fieldName the name of the field. * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java index 0a136d0b..e8b1deb4 100644 --- a/src/core/lombok/eclipse/handlers/HandleVal.java +++ b/src/core/lombok/eclipse/handlers/HandleVal.java @@ -21,18 +21,14 @@ */ package lombok.eclipse.handlers; -import static lombok.core.handlers.HandlerUtil.*; -import static lombok.eclipse.handlers.EclipseHandlerUtil.typeMatches; - import lombok.ConfigurationKeys; -import lombok.val; import lombok.core.HandlerPriority; import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseASTAdapter; import lombok.eclipse.EclipseASTVisitor; import lombok.eclipse.EclipseNode; - import lombok.experimental.var; +import lombok.val; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.ForStatement; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; @@ -40,6 +36,9 @@ import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.mangosdk.spi.ProviderFor; +import static lombok.core.handlers.HandlerUtil.handleFlagUsage; +import static lombok.eclipse.handlers.EclipseHandlerUtil.typeMatches; + /* * This class just handles 3 basic error cases. The real meat of eclipse 'val' support is in {@code PatchVal} and {@code PatchValEclipse}. */ @@ -51,34 +50,34 @@ public class HandleVal extends EclipseASTAdapter { TypeReference type = local.type; boolean isVal = typeMatches(val.class, localNode, type); boolean isVar = typeMatches(var.class, localNode, type); - if (!(isVal ||isVar)) return; - + if (!(isVal || isVar)) return; + if (isVal) handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); if (isVar) handleFlagUsage(localNode, ConfigurationKeys.VAR_FLAG_USAGE, "var"); - + boolean variableOfForEach = false; - + if (localNode.directUp().get() instanceof ForeachStatement) { ForeachStatement fs = (ForeachStatement) localNode.directUp().get(); variableOfForEach = fs.elementVariable == local; } - + String annotation = isVal ? "val" : "var"; if (local.initialization == null && !variableOfForEach) { localNode.addError("'" + annotation + "' on a local variable requires an initializer expression"); return; } - + if (local.initialization instanceof ArrayInitializer) { localNode.addError("'" + annotation + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); return; } - + if (isVal && localNode.directUp().get() instanceof ForStatement) { localNode.addError("'val' is not allowed in old-style for loops"); return; } - + if (local.initialization != null && local.initialization.getClass().getName().equals("org.eclipse.jdt.internal.compiler.ast.LambdaExpression")) { localNode.addError("'" + annotation + "' is not allowed with lambda expressions."); } diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 5b90ff62..47965573 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -21,22 +21,31 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; +import static lombok.javac.handlers.JavacHandlerUtil.*; +import lombok.ConfigurationKeys; +import lombok.experimental.var; +import lombok.val; +import lombok.core.HandlerPriority; +import lombok.javac.JavacASTAdapter; +import lombok.javac.JavacASTVisitor; +import lombok.javac.JavacNode; +import lombok.javac.JavacResolution; +import lombok.javac.ResolutionResetNeeded; + +import org.mangosdk.spi.ProviderFor; + import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCForLoop; +import com.sun.tools.javac.tree.JCTree.JCNewArray; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; -import lombok.ConfigurationKeys; -import lombok.core.HandlerPriority; -import lombok.javac.*; -import lombok.val; -import lombok.experimental.var; -import org.mangosdk.spi.ProviderFor; - -import static lombok.core.handlers.HandlerUtil.handleFlagUsage; -import static lombok.javac.handlers.JavacHandlerUtil.recursiveSetGeneratedBy; -import static lombok.javac.handlers.JavacHandlerUtil.typeMatches; @ProviderFor(JavacASTVisitor.class) @HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated. @@ -98,13 +107,13 @@ public class HandleVal extends JavacASTAdapter { JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.nil()), typeTree, localNode.getContext()); local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation); } - + if (JavacResolution.platformHasTargetTyping()) { local.vartype = localNode.getAst().getTreeMaker().Ident(localNode.getAst().toName("___Lombok_VAL_Attrib__")); } else { local.vartype = JavacResolution.createJavaLangObject(localNode.getAst()); } - + Type type; try { if (rhsOfEnhancedForLoop == null) { @@ -137,10 +146,10 @@ public class HandleVal extends JavacASTAdapter { type = rhsOfEnhancedForLoop.type; } } - + try { JCExpression replacement; - + if (rhsOfEnhancedForLoop != null) { Type componentType = JavacResolution.ifTypeIsIterableToComponent(type, localNode.getAst()); if (componentType == null) replacement = JavacResolution.createJavaLangObject(localNode.getAst()); @@ -148,7 +157,7 @@ public class HandleVal extends JavacASTAdapter { } else { replacement = JavacResolution.typeToJCTree(type, localNode.getAst(), false); } - + if (replacement != null) { local.vartype = replacement; } else { diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index 43e93bbe..e4dd7b26 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -21,9 +21,22 @@ */ package lombok.eclipse.agent; -import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import java.lang.reflect.Field; @@ -31,13 +44,13 @@ import static lombok.eclipse.Eclipse.poss; import static lombok.eclipse.handlers.EclipseHandlerUtil.makeType; public class PatchVal { - + // This is half of the work for 'val' support - the other half is in PatchValEclipse. This half is enough for ecj. // Creates a copy of the 'initialization' field on a LocalDeclaration if the type of the LocalDeclaration is 'val', because the completion parser will null this out, // which in turn stops us from inferring the intended type for 'val x = 5;'. We look at the copy. // Also patches local declaration to not call .resolveType() on the initializer expression if we've already done so (calling it twice causes weird errors), // and patches .resolve() on LocalDeclaration itself to just-in-time replace the 'val' vartype with the right one. - + public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) { if (expr.resolvedType != null) return expr.resolvedType; try { @@ -49,7 +62,7 @@ public class PatchVal { return null; } } - + public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) { if (decl != null && LocalDeclaration.class.equals(decl.getClass()) && expr.resolvedType != null) return expr.resolvedType; try { @@ -61,23 +74,23 @@ public class PatchVal { return null; } } - + public static boolean matches(String key, char[] array) { if (array == null || key.length() != array.length) return false; for (int i = 0; i < array.length; i++) { if (key.charAt(i) != array[i]) return false; } - + return true; } - + public static boolean couldBe(String key, TypeReference ref) { String[] keyParts = key.split("\\."); if (ref instanceof SingleTypeReference) { char[] token = ((SingleTypeReference)ref).token; return matches(keyParts[keyParts.length - 1], token); } - + if (ref instanceof QualifiedTypeReference) { char[][] tokens = ((QualifiedTypeReference)ref).tokens; if (keyParts.length != tokens.length) return false; @@ -88,7 +101,7 @@ public class PatchVal { } return true; } - + return false; } @@ -110,20 +123,20 @@ public class PatchVal { System.arraycopy(nm, 0, fullName, pkgFullLength, nm.length); return matches(key, fullName); } - + public static final class Reflection { private static final Field initCopyField, iterableCopyField; - + static { Field a = null, b = null; - + try { a = LocalDeclaration.class.getDeclaredField("$initCopy"); b = LocalDeclaration.class.getDeclaredField("$iterableCopy"); } catch (Throwable t) { //ignore - no $initCopy exists when running in ecj. } - + initCopyField = a; iterableCopyField = b; } @@ -131,11 +144,11 @@ public class PatchVal { public static boolean handleValForLocalDeclaration(LocalDeclaration local, BlockScope scope) { if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false; boolean decomponent = false; - + boolean val = isVal(local, scope); boolean var = isVar(local, scope); if (!(val || var)) return false; - + StackTraceElement[] st = new Throwable().getStackTrace(); for (int i = 0; i < st.length - 2 && i < 10; i++) { if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) { @@ -146,7 +159,7 @@ public class PatchVal { break; } } - + Expression init = local.initialization; if (init == null && Reflection.initCopyField != null) { try { @@ -155,7 +168,7 @@ public class PatchVal { // init remains null. } } - + if (init == null && Reflection.iterableCopyField != null) { try { init = (Expression) Reflection.iterableCopyField.get(local); @@ -190,7 +203,7 @@ public class PatchVal { } } } - + if(val) local.modifiers |= ClassFileConstants.AccFinal; local.annotations = addValAnnotation(local.annotations, local.type, scope); local.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(local.type, 3)); @@ -212,16 +225,16 @@ public class PatchVal { boolean val = isVal(forEach.elementVariable, scope); boolean var = isVar(forEach.elementVariable, scope); if (!(val || var)) return false; - + TypeBinding component = getForEachComponentType(forEach.collection, scope); if (component == null) return false; TypeReference replacement = makeType(component, forEach.elementVariable.type, false); - + if (val) forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal; forEach.elementVariable.annotations = addValAnnotation(forEach.elementVariable.annotations, forEach.elementVariable.type, scope); forEach.elementVariable.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(forEach.elementVariable.type, 3)); - + return false; } @@ -233,12 +246,12 @@ public class PatchVal { } else { newAnn = new Annotation[1]; } - + newAnn[newAnn.length - 1] = new org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation(originalRef, originalRef.sourceStart); - + return newAnn; } - + private static TypeBinding getForEachComponentType(Expression collection, BlockScope scope) { if (collection != null) { TypeBinding resolved = collection.resolvedType; @@ -249,7 +262,7 @@ public class PatchVal { return resolved; } else if (resolved instanceof ReferenceBinding) { ReferenceBinding iterableType = ((ReferenceBinding)resolved).findSuperTypeOriginatingFrom(TypeIds.T_JavaLangIterable, false); - + TypeBinding[] arguments = null; if (iterableType != null) switch (iterableType.kind()) { case Binding.GENERIC_TYPE : // for (T t : Iterable) - in case used inside Iterable itself @@ -261,16 +274,16 @@ public class PatchVal { case Binding.RAW_TYPE : // for(Object e : Iterable) return null; } - + if (arguments != null && arguments.length == 1) { return arguments[0]; } } } - + return null; } - + private static TypeBinding resolveForExpression(Expression collection, BlockScope scope) { try { return collection.resolveType(scope); diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java index d0c29ac8..505eb767 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java @@ -41,7 +41,13 @@ import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; -import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.parser.Parser; -- cgit From 69eeb9edc767bb3ceb0320bb5a0ea60dfabe827c Mon Sep 17 00:00:00 2001 From: Bulgakov Alexander Date: Sat, 12 Nov 2016 23:04:48 +0300 Subject: a initialization of variable like "var o = null;" will throw the compile time error "variable initializer is 'null'" --- src/core/lombok/eclipse/handlers/HandleVal.java | 9 +++++++++ src/core/lombok/javac/handlers/HandleVal.java | 10 +++++++++- test/transform/resource/after-delombok/VarNullInit.java | 3 ++- test/transform/resource/after-ecj/VarNullInit.java | 5 ++--- test/transform/resource/before/VarNullInit.java | 2 +- .../resource/messages-delombok/VarNullInit.java.messages | 1 + test/transform/resource/messages-ecj/VarNullInit.java.messages | 1 + 7 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 test/transform/resource/messages-delombok/VarNullInit.java.messages create mode 100644 test/transform/resource/messages-ecj/VarNullInit.java.messages diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java index e8b1deb4..49820f60 100644 --- a/src/core/lombok/eclipse/handlers/HandleVal.java +++ b/src/core/lombok/eclipse/handlers/HandleVal.java @@ -23,6 +23,7 @@ package lombok.eclipse.handlers; import lombok.ConfigurationKeys; import lombok.core.HandlerPriority; +import lombok.core.LombokNode; import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseASTAdapter; import lombok.eclipse.EclipseASTVisitor; @@ -33,11 +34,13 @@ import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.ForStatement; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.mangosdk.spi.ProviderFor; import static lombok.core.handlers.HandlerUtil.handleFlagUsage; import static lombok.eclipse.handlers.EclipseHandlerUtil.typeMatches; +import static lombok.javac.handlers.HandleVal.VARIABLE_INITIALIZER_IS_NULL; /* * This class just handles 3 basic error cases. The real meat of eclipse 'val' support is in {@code PatchVal} and {@code PatchValEclipse}. @@ -81,5 +84,11 @@ public class HandleVal extends EclipseASTAdapter { if (local.initialization != null && local.initialization.getClass().getName().equals("org.eclipse.jdt.internal.compiler.ast.LambdaExpression")) { localNode.addError("'" + annotation + "' is not allowed with lambda expressions."); } + + if(isVar && local.initialization instanceof NullLiteral) addVarNullInitMessage(localNode); + } + + public static void addVarNullInitMessage(LombokNode localNode) { + localNode.addError(VARIABLE_INITIALIZER_IS_NULL); } } diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 47965573..2ff3bf38 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -22,7 +22,10 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.*; +import static lombok.eclipse.handlers.HandleVal.addVarNullInitMessage; import static lombok.javac.handlers.JavacHandlerUtil.*; + +import com.sun.tools.javac.tree.JCTree.JCLiteral; import lombok.ConfigurationKeys; import lombok.experimental.var; import lombok.val; @@ -51,7 +54,9 @@ import com.sun.tools.javac.util.List; @HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated. @ResolutionResetNeeded public class HandleVal extends JavacASTAdapter { - + + public static final String VARIABLE_INITIALIZER_IS_NULL = "variable initializer is 'null'"; + private static boolean eq(String typeTreeToString, String key) { return (typeTreeToString.equals(key) || typeTreeToString.equals("lombok." + key)); } @@ -118,6 +123,9 @@ public class HandleVal extends JavacASTAdapter { try { if (rhsOfEnhancedForLoop == null) { if (local.init.type == null) { + if (isVar && local.init instanceof JCLiteral && ((JCLiteral) local.init).value == null) { + addVarNullInitMessage(localNode); + } JavacResolution resolver = new JavacResolution(localNode.getContext()); try { type = ((JCExpression) resolver.resolveMethodMember(localNode).get(local.init)).type; diff --git a/test/transform/resource/after-delombok/VarNullInit.java b/test/transform/resource/after-delombok/VarNullInit.java index f0087765..8ec2ea73 100644 --- a/test/transform/resource/after-delombok/VarNullInit.java +++ b/test/transform/resource/after-delombok/VarNullInit.java @@ -1,4 +1,5 @@ -class VarNullInit { + +public class VarNullInit { void method() { java.lang.Object x = null; } diff --git a/test/transform/resource/after-ecj/VarNullInit.java b/test/transform/resource/after-ecj/VarNullInit.java index ba8c3e46..3eb2d506 100644 --- a/test/transform/resource/after-ecj/VarNullInit.java +++ b/test/transform/resource/after-ecj/VarNullInit.java @@ -1,7 +1,6 @@ import lombok.experimental.var; - -class VarNullInit { - VarNullInit() { +public class VarNullInit { + public VarNullInit() { super(); } void method() { diff --git a/test/transform/resource/before/VarNullInit.java b/test/transform/resource/before/VarNullInit.java index f7f9c55b..efdc9d9e 100644 --- a/test/transform/resource/before/VarNullInit.java +++ b/test/transform/resource/before/VarNullInit.java @@ -1,7 +1,7 @@ //CONF: lombok.var.flagUsage = ALLOW import lombok.experimental.var; -class VarNullInit { +public class VarNullInit { void method() { var x = null; } diff --git a/test/transform/resource/messages-delombok/VarNullInit.java.messages b/test/transform/resource/messages-delombok/VarNullInit.java.messages new file mode 100644 index 00000000..190ab7c4 --- /dev/null +++ b/test/transform/resource/messages-delombok/VarNullInit.java.messages @@ -0,0 +1 @@ +6 variable initializer is 'null' \ No newline at end of file diff --git a/test/transform/resource/messages-ecj/VarNullInit.java.messages b/test/transform/resource/messages-ecj/VarNullInit.java.messages new file mode 100644 index 00000000..190ab7c4 --- /dev/null +++ b/test/transform/resource/messages-ecj/VarNullInit.java.messages @@ -0,0 +1 @@ +6 variable initializer is 'null' \ No newline at end of file -- cgit