aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buildScripts/website.ant.xml3
-rw-r--r--src/core/lombok/ConfigurationKeys.java3
-rw-r--r--src/core/lombok/core/configuration/AllowHelper.java37
-rw-r--r--src/core/lombok/core/configuration/FlagUsageType.java2
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java10
-rw-r--r--src/core/lombok/eclipse/handlers/HandleVal.java36
-rw-r--r--src/core/lombok/experimental/var.java28
-rw-r--r--src/core/lombok/javac/handlers/HandleVal.java58
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchVal.java67
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java19
-rw-r--r--test/transform/resource/after-delombok/ValNullInit.java5
-rw-r--r--test/transform/resource/after-delombok/VarComplex.java20
-rw-r--r--test/transform/resource/after-delombok/VarInFor.java9
-rw-r--r--test/transform/resource/after-delombok/VarInForOld.java7
-rw-r--r--test/transform/resource/after-delombok/VarModifier.java7
-rw-r--r--test/transform/resource/after-delombok/VarNullInit.java6
-rw-r--r--test/transform/resource/after-ecj/ValNullInit.java10
-rw-r--r--test/transform/resource/after-ecj/VarComplex.java26
-rw-r--r--test/transform/resource/after-ecj/VarInFor.java14
-rw-r--r--test/transform/resource/after-ecj/VarInForOld.java12
-rw-r--r--test/transform/resource/after-ecj/VarModifier.java12
-rw-r--r--test/transform/resource/after-ecj/VarNullInit.java9
-rw-r--r--test/transform/resource/before/ValNullInit.java7
-rw-r--r--test/transform/resource/before/VarComplex.java23
-rw-r--r--test/transform/resource/before/VarInFor.java12
-rw-r--r--test/transform/resource/before/VarInForOld.java10
-rw-r--r--test/transform/resource/before/VarModifier.java11
-rw-r--r--test/transform/resource/before/VarNullInit.java8
-rw-r--r--test/transform/resource/messages-delombok/VarNullInit.java.messages1
-rw-r--r--test/transform/resource/messages-ecj/VarNullInit.java.messages1
-rw-r--r--usage_examples/experimental/varExample_post.jpage21
-rw-r--r--usage_examples/experimental/varExample_pre.jpage21
-rw-r--r--website/features/experimental/index.html2
-rw-r--r--website/features/experimental/var.html83
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 &copy; 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>