diff options
34 files changed, 548 insertions, 52 deletions
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 @@ -155,6 +155,9 @@ such as converting the changelog into HTML, and creating javadoc. <param name="transformationName" value="Singular-snippet" /> </antcall> <antcall target="-integrateSnippet"> + <param name="transformationName" value="experimental/var" /> + </antcall> + <antcall target="-integrateSnippet"> <param name="transformationName" value="experimental/Delegate" /> </antcall> <antcall target="-integrateSnippet"> 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, <em>any</em> usage of {@code val} results in a warning / error. */ public static final ConfigurationKey<FlagUsageType> VAL_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.val.flagUsage", "Emit a warning or error if 'val' is used.") {}; - + public static final ConfigurationKey<FlagUsageType> VAR_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.var.flagUsage", "Emit a warning or error if 'var' is used.") {}; + // ##### Extern ##### // ----- Logging ----- 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<String> allowable = new HashSet<String>(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..a321e67f 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -43,6 +43,7 @@ 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; @@ -95,12 +96,19 @@ public class HandlerUtil { return Singulars.autoSingularize(plural); } public static void handleFlagUsage(LombokNode<?, ?, ?> node, ConfigurationKey<FlagUsageType> key, String featureName) { + boolean allowable = AllowHelper.isAllowable(featureName); + FlagUsageType fut = node.getAst().readConfiguration(key); + boolean allowed = !allowable || FlagUsageType.ALLOW == fut; + if (!allowed) { + 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 == FlagUsageType.WARNING) node.addWarning(msg); - else node.addError(msg); + else if (fut == FlagUsageType.ERROR) node.addError(msg); } } diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java index d4ae417c..49820f60 100644 --- a/src/core/lombok/eclipse/handlers/HandleVal.java +++ b/src/core/lombok/eclipse/handlers/HandleVal.java @@ -21,21 +21,27 @@ */ package lombok.eclipse.handlers; -import static lombok.core.handlers.HandlerUtil.*; import lombok.ConfigurationKeys; -import lombok.val; import lombok.core.HandlerPriority; +import lombok.core.LombokNode; 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; 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}. */ @@ -44,8 +50,13 @@ 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; @@ -54,23 +65,30 @@ public class HandleVal extends EclipseASTAdapter { 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."); } + + 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/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 337ab2d7..2ff3bf38 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -22,8 +22,12 @@ 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; import lombok.core.HandlerPriority; import lombok.javac.JavacASTAdapter; @@ -50,21 +54,33 @@ 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 { - @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) { + + 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)); + } + + @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,21 +88,26 @@ 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, val.class.getName()); + JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, var.class.getName()); + } + + if (isVal) local.mods.flags |= Flags.FINAL; + if (!localNode.shouldDeleteLombokAnnotations()) { JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), typeTree, localNode.getContext()); local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation); @@ -102,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/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index 2b8dfbaa..e4dd7b26 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,6 +38,11 @@ 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. @@ -84,31 +84,44 @@ public class PatchVal { return true; } - public static boolean couldBeVal(TypeReference ref) { + public static boolean couldBe(String key, TypeReference ref) { + String[] keyParts = key.split("\\."); if (ref instanceof SingleTypeReference) { char[] token = ((SingleTypeReference)ref).token; - return matches("val", 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("val", 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; } - - 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); + 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,13 +145,17 @@ public class PatchVal { if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false; boolean decomponent = false; - if (!isVal(local.type, scope)) return 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")) { - 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; } } @@ -187,23 +204,33 @@ 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)); 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; - if (!isVal(forEach.elementVariable.type, scope)) return false; + 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); - 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)); diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java index 7d5f36f4..505eb767 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java @@ -47,6 +47,7 @@ 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; @@ -65,7 +66,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 = couldBeVal(foreachDecl.elementVariable.type); + boolean var = couldBeVar(foreachDecl.elementVariable.type); + if (foreachDecl.elementVariable == null || !(val || var)) return; try { if (Reflection.iterableCopyField != null) Reflection.iterableCopyField.set(foreachDecl.elementVariable, init); @@ -88,7 +91,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 = couldBeVal(variableDecl.type); + boolean var = couldBeVar(variableDecl.type); + if (!(val || var)) return; try { if (Reflection.initCopyField != null) Reflection.initCopyField.set(variableDecl, init); @@ -97,6 +102,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<IExtendedModifier> modifiers = out.modifiers(); addFinalAndValAnnotationToModifierList(converter, modifiers, out.getAST(), in); @@ -115,7 +124,7 @@ public class PatchValEclipse { Annotation valAnnotation = null; for (Annotation ann : in.annotations) { - if (PatchVal.couldBeVal(ann.type)) { + if (couldBeVal(ann.type)) { found = true; valAnnotation = ann; break; @@ -167,6 +176,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/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-delombok/VarNullInit.java b/test/transform/resource/after-delombok/VarNullInit.java new file mode 100644 index 00000000..8ec2ea73 --- /dev/null +++ b/test/transform/resource/after-delombok/VarNullInit.java @@ -0,0 +1,6 @@ + +public 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 new file mode 100644 index 00000000..10c456eb --- /dev/null +++ b/test/transform/resource/after-ecj/VarComplex.java @@ -0,0 +1,26 @@ +import lombok.experimental.var; +public class VarComplex { + private String field = ""; + private static final int CONSTANT = 20; + <clinit>() { + } + 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..0192aaed --- /dev/null +++ b/test/transform/resource/after-ecj/VarInFor.java @@ -0,0 +1,14 @@ +import lombok.experimental.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..98fedf03 --- /dev/null +++ b/test/transform/resource/after-ecj/VarInForOld.java @@ -0,0 +1,12 @@ +import lombok.experimental.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..d11142ca --- /dev/null +++ b/test/transform/resource/after-ecj/VarModifier.java @@ -0,0 +1,12 @@ +import lombok.experimental.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/after-ecj/VarNullInit.java b/test/transform/resource/after-ecj/VarNullInit.java new file mode 100644 index 00000000..3eb2d506 --- /dev/null +++ b/test/transform/resource/after-ecj/VarNullInit.java @@ -0,0 +1,9 @@ +import lombok.experimental.var; +public class VarNullInit { + public VarNullInit() { + super(); + } + void method() { + @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 new file mode 100644 index 00000000..bfaa8804 --- /dev/null +++ b/test/transform/resource/before/VarComplex.java @@ -0,0 +1,23 @@ +//CONF: lombok.var.flagUsage = ALLOW +import lombok.experimental.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..cc8c387e --- /dev/null +++ b/test/transform/resource/before/VarInFor.java @@ -0,0 +1,12 @@ +//CONF: lombok.var.flagUsage = ALLOW +import lombok.experimental.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..f90aba7f --- /dev/null +++ b/test/transform/resource/before/VarInForOld.java @@ -0,0 +1,10 @@ +//CONF: lombok.var.flagUsage = ALLOW +import lombok.experimental.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..7250c1c5 --- /dev/null +++ b/test/transform/resource/before/VarModifier.java @@ -0,0 +1,11 @@ +//CONF: lombok.var.flagUsage = ALLOW +import lombok.experimental.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 diff --git a/test/transform/resource/before/VarNullInit.java b/test/transform/resource/before/VarNullInit.java new file mode 100644 index 00000000..efdc9d9e --- /dev/null +++ b/test/transform/resource/before/VarNullInit.java @@ -0,0 +1,8 @@ +//CONF: lombok.var.flagUsage = ALLOW +import lombok.experimental.var; + +public class VarNullInit { + void method() { + var x = null; + } +}
\ No newline at end of file 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 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<String>(); + example.add("Hello, World!"); + final String foo = example.get(0); + return foo.toLowerCase(); + } + + public void example2() { + ArrayList list = new ArrayList<String>(); + 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<String>(); + example.add("Hello, World!"); + final var foo = example.get(0); + return foo.toLowerCase(); + } + + public void example2() { + var list = new ArrayList<String>(); + 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. <dl> + <dt><a href="var.html"><code>@var</code></a></dt> + <dd>The same as @val but modifiable.</dd> <dt><a href="Accessors.html"><code>@Accessors</code></a></dt> <dd>A more fluent API for getters and setters.</dd> <dt><a href="ExtensionMethod.html"><code>@ExtensionMethod</code></a></dt> 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 @@ +<!DOCTYPE html> +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <link rel="stylesheet" type="text/css" href="../../logi/reset.css" /> + <link rel="stylesheet" type="text/css" href="../features.css" /> + <link rel="shortcut icon" href="../../favicon.ico" type="image/x-icon" /> + <meta name="description" content="Spice up your java" /> + <title>EXPERIMENTAL – @var</title> +</head><body><div id="pepper"> + <div class="minimumHeight"></div> + <div class="meat"> + <div class="header"><a href="../../index.html">Project Lombok</a></div> + <h1>@var</h1> + <div class="byline">Modifiable local variables with a type inferred by assigning value.</div> + <div class="since"> + <h3>Since</h3> + <p> + @var was introduced as experimental feature in lombok v1.16.11. + </p> + </div> + <div class="experimental"> + <h3>Experimental</h3> + <p> + Experimental because: + <ul> + <li>This feature is very controversial.</li> + <li>There is <a href="http://openjdk.java.net/jeps/286">JEP 286</a> that should make the @var obsolete.</li> + </ul>Current status: <em>uncertain</em> - Currently we feel this feature cannot move out of experimental status. + </div> + <div class="overview"> + <h3>Overview</h3> + <p> + The <code>@var</code> has the same mission as the <a href="../val.html">@val</a> annotation but modifable. + </p> + </div> + <div class="snippets"> + <div class="pre"> + <h3>With Lombok</h3> + <div class="snippet">@HTML_PRE@</div> + </div> + <div class="sep"></div> + <div class="post"> + <h3>Vanilla Java</h3> + <div class="snippet">@HTML_POST@</div> + </div> + </div> + <div style="clear: left;"></div> + <div class="overview confKeys"> + <h3>Supported configuration keys:</h3> + <dl> + <dt><code>lombok.val.flagUsage</code> = [<code>allow</code>] (default: not set)</dt> + <dd>Lombok will flag any usage of <code>var</code> as a error if not configured.</dd> + </dl> + </div> + <div class="overview"> + <h3>Small print</h3><div class="smallprint"> + <p> + For compound types, the most common superclass is inferred, not any shared interfaces. For example, <code>bool ? new HashSet() : new ArrayList()</code> + is an expression with a compound type: The result is both <code>AbstractCollection</code> as well as <code>Serializable</code>. The type inferred will be + <code>AbstractCollection</code>, as that is a class, whereas <code>Serializable</code> is an interface. + </p><p> + In ambiguous cases, such as when the initializer expression is <code>null</code>, <code>java.lang.Object</code> is inferred. + </p> + </div> + </div> + <div class="footer"> + <a href="index.html">Back to experimental features</a> | <span class="disabled">Previous feature</span> | <a href="ExtensionMethod.html">Next feature (@ExtensionMethod)</a><br /> + <a href="../../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2016 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> + </div> + <div style="clear: both;"></div> + </div> +</div> +<script type="text/javascript"> + var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); + document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); +</script> +<script type="text/javascript"> + try { + var pageTracker = _gat._getTracker("UA-9884254-1"); + pageTracker._trackPageview(); + } catch(err) {} +</script> +</body></html> |