aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/changelog.markdown1
-rw-r--r--src/core/lombok/SneakyThrows.java4
-rw-r--r--src/core/lombok/eclipse/handlers/HandleLog.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleVal.java20
-rw-r--r--src/core/lombok/javac/JavacAST.java2
-rw-r--r--src/core/lombok/javac/JavacResolution.java60
-rw-r--r--src/core/lombok/javac/handlers/HandleLog.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleSetter.java1
-rw-r--r--src/core/lombok/javac/handlers/HandleVal.java6
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchVal.java12
-rwxr-xr-xsrc/eclipseAgent/lombok/launch/PatchFixesHider.java1
-rw-r--r--src/installer/lombok/installer/IdeLocation.java12
-rw-r--r--src/installer/lombok/installer/eclipse/EclipseProductLocation.java4
-rw-r--r--src/stubs/com/sun/tools/javac/comp/ArgumentAttr.java12
-rw-r--r--src/stubsstubs/com/sun/tools/javac/tree/JCTree.java1
-rw-r--r--src/utils/lombok/eclipse/Eclipse.java7
-rw-r--r--src/utils/lombok/javac/TreeMirrorMaker.java35
-rw-r--r--src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java3
-rw-r--r--test/transform/resource/after-delombok/ValAnonymousSubclassSelfReference.java17
-rw-r--r--test/transform/resource/after-delombok/ValSwitchExpression.java11
-rw-r--r--test/transform/resource/after-delombok/ValToNative.java15
-rw-r--r--test/transform/resource/after-ecj/ValAnonymousSubclassSelfReference.java23
-rw-r--r--test/transform/resource/after-ecj/ValSwitchExpression.java16
-rw-r--r--test/transform/resource/after-ecj/ValToNative.java19
-rw-r--r--test/transform/resource/before/MixGetterVal.java1
-rw-r--r--test/transform/resource/before/TrickyTypeResolution.java1
-rw-r--r--test/transform/resource/before/ValAnonymousSubclassSelfReference.java21
-rw-r--r--test/transform/resource/before/ValAnonymousSubclassWithGenerics.java1
-rw-r--r--test/transform/resource/before/ValComplex.java1
-rw-r--r--test/transform/resource/before/ValDefault.java2
-rw-r--r--test/transform/resource/before/ValDelegateMethodReference.java2
-rw-r--r--test/transform/resource/before/ValErrors.java1
-rw-r--r--test/transform/resource/before/ValFinal.java1
-rw-r--r--test/transform/resource/before/ValInBasicFor.java1
-rw-r--r--test/transform/resource/before/ValInFor.java1
-rw-r--r--test/transform/resource/before/ValInLambda.java2
-rw-r--r--test/transform/resource/before/ValInMultiDeclaration.java1
-rw-r--r--test/transform/resource/before/ValInTryWithResources.java2
-rw-r--r--test/transform/resource/before/ValLambda.java2
-rw-r--r--test/transform/resource/before/ValLessSimple.java1
-rw-r--r--test/transform/resource/before/ValLub.java1
-rw-r--r--test/transform/resource/before/ValNullInit.java1
-rw-r--r--test/transform/resource/before/ValOutersWithGenerics.java1
-rw-r--r--test/transform/resource/before/ValRawType.java1
-rw-r--r--test/transform/resource/before/ValSimple.java1
-rw-r--r--test/transform/resource/before/ValSwitchExpression.java13
-rw-r--r--test/transform/resource/before/ValToNative.java19
-rw-r--r--test/transform/resource/before/ValWeirdTypes.java2
-rw-r--r--test/transform/resource/before/ValWithLabel.java1
-rw-r--r--test/transform/resource/before/ValWithLocalClasses.java1
-rw-r--r--test/transform/resource/before/ValWithSelfRefGenerics.java1
-rw-r--r--website/resources/css/custom.css4
-rw-r--r--website/templates/features/Builder.html24
-rw-r--r--website/templates/features/val.html3
54 files changed, 355 insertions, 45 deletions
diff --git a/doc/changelog.markdown b/doc/changelog.markdown
index 583736f1..973d86ea 100644
--- a/doc/changelog.markdown
+++ b/doc/changelog.markdown
@@ -3,6 +3,7 @@ Lombok Changelog
### v1.18.21 "Edgy Guinea Pig"
* Added the `@StandardException` feature. [Pull Request #2702](https://github.com/projectlombok/lombok/pull/2702).
+* IMPROBABLE BREAKING CHANGE: If the underlying compiler and `--release` / `--source` option is 10 or higher, lombok's `val` is now replaced by `final var`. That means compound declarations such as `val x = 10, y = 12;` now fail (lombok's old `val` implementation supported it, javac's `var` does not), but IDE support in particular is more reliable. We decided it was worth the tradeoff.
### v1.18.20 (April 2nd, 2021)
* PLATFORM: JDK16 support added. [Issue #2681](https://github.com/projectlombok/lombok/issues/2681).
diff --git a/src/core/lombok/SneakyThrows.java b/src/core/lombok/SneakyThrows.java
index 0506d615..2a8009a3 100644
--- a/src/core/lombok/SneakyThrows.java
+++ b/src/core/lombok/SneakyThrows.java
@@ -39,14 +39,14 @@ import java.lang.annotation.Target;
* Example:
* <pre>
* &#64;SneakyThrows(UnsupportedEncodingException.class)
- * public void utf8ToString(byte[] bytes) {
+ * public String utf8ToString(byte[] bytes) {
* return new String(bytes, "UTF-8");
* }
* </pre>
*
* Becomes:
* <pre>
- * public void utf8ToString(byte[] bytes) {
+ * public String utf8ToString(byte[] bytes) {
* try {
* return new String(bytes, "UTF-8");
* } catch (UnsupportedEncodingException $uniqueName) {
diff --git a/src/core/lombok/eclipse/handlers/HandleLog.java b/src/core/lombok/eclipse/handlers/HandleLog.java
index 38fdb440..db9104ef 100644
--- a/src/core/lombok/eclipse/handlers/HandleLog.java
+++ b/src/core/lombok/eclipse/handlers/HandleLog.java
@@ -307,7 +307,7 @@ public class HandleLog {
handleFlagUsage(annotationNode, ConfigurationKeys.LOG_CUSTOM_FLAG_USAGE, "@CustomLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log");
LogDeclaration logDeclaration = annotationNode.getAst().readConfiguration(ConfigurationKeys.LOG_CUSTOM_DECLARATION);
if (logDeclaration == null) {
- annotationNode.addError("The @CustomLog annotation is not configured; please set log.custom.declaration in lombok.config.");
+ annotationNode.addError("The @CustomLog annotation is not configured; please set lombok.log.custom.declaration in lombok.config.");
return;
}
LoggingFramework framework = new LoggingFramework(lombok.CustomLog.class, logDeclaration);
diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java
index ac53e27e..0f70e66d 100644
--- a/src/core/lombok/eclipse/handlers/HandleVal.java
+++ b/src/core/lombok/eclipse/handlers/HandleVal.java
@@ -22,12 +22,14 @@
package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.handleFlagUsage;
-import static lombok.eclipse.handlers.EclipseHandlerUtil.typeMatches;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
import lombok.ConfigurationKeys;
import lombok.val;
import lombok.var;
import lombok.core.HandlerPriority;
import lombok.eclipse.DeferUntilPostDiet;
+import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseASTAdapter;
import lombok.eclipse.EclipseASTVisitor;
import lombok.eclipse.EclipseNode;
@@ -39,10 +41,13 @@ 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.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
/*
- * This class just handles 3 basic error cases. The real meat of eclipse 'val' support is in {@code PatchVal} and {@code PatchValEclipse}.
+ * Java 1-9: This class just handles 3 basic error cases. The real meat of eclipse 'val' support is in {@code PatchVal} and {@code PatchValEclipse}.
+ * Java 10+: Lombok uses the native 'var' support and transforms 'val' to 'final var'.
*/
@Provides(EclipseASTVisitor.class)
@DeferUntilPostDiet
@@ -96,5 +101,16 @@ public class HandleVal extends EclipseASTAdapter {
localNode.addError("variable initializer is 'null'");
return;
}
+
+ // For Java >= 10 we use native support
+ if (localNode.getSourceVersion() >= 10) {
+ if (isVal) {
+ TypeReference originalType = local.type;
+ local.type = new SingleTypeReference("var".toCharArray(), Eclipse.pos(local.type));
+ local.modifiers |= ClassFileConstants.AccFinal;
+ local.annotations = addAnnotation(local.type, local.annotations, originalType.getTypeName());
+ }
+ return;
+ }
}
}
diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java
index f58de60f..0919c7d4 100644
--- a/src/core/lombok/javac/JavacAST.java
+++ b/src/core/lombok/javac/JavacAST.java
@@ -228,7 +228,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
int underscoreIdx = nm.indexOf('_');
if (underscoreIdx > -1) return Integer.parseInt(nm.substring(underscoreIdx + 1));
// assume java9+
- return Integer.parseInt(nm);
+ return Integer.parseInt(nm.substring(3));
} catch (Exception ignore) {}
return 6;
}
diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java
index e96079e0..f1109f4e 100644
--- a/src/core/lombok/javac/JavacResolution.java
+++ b/src/core/lombok/javac/JavacResolution.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2020 The Project Lombok Authors.
+ * Copyright (C) 2011-2021 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
@@ -29,6 +29,7 @@ import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -45,6 +46,7 @@ import com.sun.tools.javac.code.Type.ClassType;
import com.sun.tools.javac.code.Type.TypeVar;
import com.sun.tools.javac.code.Type.WildcardType;
import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.comp.ArgumentAttr;
import com.sun.tools.javac.comp.Attr;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Enter;
@@ -68,6 +70,7 @@ import lombok.core.debug.AssertionLogger;
import lombok.permit.Permit;
public class JavacResolution {
+ private final Context context;
private final Attr attr;
private final CompilerMessageSuppressor messageSuppressor;
@@ -82,6 +85,7 @@ public class JavacResolution {
}
public JavacResolution(Context context) {
+ this.context = context;
attr = Attr.instance(context);
messageSuppressor = new CompilerMessageSuppressor(context);
}
@@ -245,10 +249,19 @@ public class JavacResolution {
} catch (Throwable ignore) {
// This addresses issue #1553 which involves JDK9; if it doesn't exist, we probably don't need to set it.
}
- if (tree instanceof JCBlock) attr.attribStat(tree, env);
- else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl) tree).body, env);
- else if (tree instanceof JCVariableDecl) attr.attribStat(tree, env);
- else throw new IllegalStateException("Called with something that isn't a block, method decl, or variable decl");
+
+ Map<?,?> cache = null;
+ try {
+ cache = ArgumentAttrReflect.enableTempCache(context);
+
+ if (tree instanceof JCBlock) attr.attribStat(tree, env);
+ else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl) tree).body, env);
+ else if (tree instanceof JCVariableDecl) attr.attribStat(tree, env);
+ else throw new IllegalStateException("Called with something that isn't a block, method decl, or variable decl");
+ } finally {
+ ArgumentAttrReflect.restoreCache(cache, context);
+ }
+
}
public static class TypeNotConvertibleException extends Exception {
@@ -283,6 +296,43 @@ public class JavacResolution {
}
}
+ /**
+ * ArgumentAttr was added in Java 9 and caches some method arguments. Lombok should cleanup its changes after resolution.
+ */
+ private static class ArgumentAttrReflect {
+ private static Field ARGUMENT_TYPE_CACHE;
+
+ static {
+ if (Javac.getJavaCompilerVersion() >= 9) {
+ try {
+ ARGUMENT_TYPE_CACHE = Permit.getField(ArgumentAttr.class, "argumentTypeCache");
+ } catch (Exception ignore) {}
+ }
+ }
+
+ public static Map<?, ?> enableTempCache(Context context) {
+ if (ARGUMENT_TYPE_CACHE == null) return null;
+
+ ArgumentAttr argumentAttr = ArgumentAttr.instance(context);
+ try {
+ Map<?, ?> cache = (Map<?, ?>) Permit.get(ARGUMENT_TYPE_CACHE, argumentAttr);
+ Permit.set(ARGUMENT_TYPE_CACHE, argumentAttr, new LinkedHashMap<Object, Object>(cache));
+ return cache;
+ } catch (Exception ignore) { }
+
+ return null;
+ }
+
+ public static void restoreCache(Map<?, ?> cache, Context context) {
+ if (ARGUMENT_TYPE_CACHE == null) return;
+
+ ArgumentAttr argumentAttr = ArgumentAttr.instance(context);
+ try {
+ Permit.set(ARGUMENT_TYPE_CACHE, argumentAttr, cache);
+ } catch (Exception ignore) { }
+ }
+ }
+
public static Type ifTypeIsIterableToComponent(Type type, JavacAST ast) {
if (type == null) return null;
Types types = Types.instance(ast.getContext());
diff --git a/src/core/lombok/javac/handlers/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java
index 7566550f..957f5581 100644
--- a/src/core/lombok/javac/handlers/HandleLog.java
+++ b/src/core/lombok/javac/handlers/HandleLog.java
@@ -265,7 +265,7 @@ public class HandleLog {
handleFlagUsage(annotationNode, ConfigurationKeys.LOG_CUSTOM_FLAG_USAGE, "@CustomLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log");
LogDeclaration logDeclaration = annotationNode.getAst().readConfiguration(ConfigurationKeys.LOG_CUSTOM_DECLARATION);
if (logDeclaration == null) {
- annotationNode.addError("The @CustomLog is not configured; please set log.custom.declaration in lombok.config.");
+ annotationNode.addError("The @CustomLog is not configured; please set lombok.log.custom.declaration in lombok.config.");
return;
}
LoggingFramework framework = new LoggingFramework(lombok.CustomLog.class, logDeclaration);
diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java
index a0634494..e5b2c062 100644
--- a/src/core/lombok/javac/handlers/HandleSetter.java
+++ b/src/core/lombok/javac/handlers/HandleSetter.java
@@ -43,7 +43,6 @@ import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCBlock;
-import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCReturn;
diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java
index 0ed831ab..d4fb1027 100644
--- a/src/core/lombok/javac/handlers/HandleVal.java
+++ b/src/core/lombok/javac/handlers/HandleVal.java
@@ -115,6 +115,12 @@ public class HandleVal extends JavacASTAdapter {
local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation);
}
+ if (localNode.getSourceVersion() >= 10) {
+ local.vartype = null;
+ localNode.getAst().setChanged();
+ return;
+ }
+
if (JavacResolution.platformHasTargetTyping()) {
local.vartype = localNode.getAst().getTreeMaker().Ident(localNode.getAst().toName("___Lombok_VAL_Attrib__"));
} else {
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
index 774e5b40..824ecefc 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
@@ -204,6 +204,8 @@ public class PatchVal {
boolean var = isVar(local, scope);
if (!(val || var)) return false;
+ if (hasNativeVarSupport(scope)) return false;
+
if (val) {
StackTraceElement[] st = new Throwable().getStackTrace();
for (int i = 0; i < st.length - 2 && i < 10; i++) {
@@ -281,6 +283,14 @@ public class PatchVal {
return is(local.type, scope, "lombok.val");
}
+ private static boolean hasNativeVarSupport(Scope scope) {
+ long sl = scope.problemReporter().options.sourceLevel >> 16;
+ long cl = scope.problemReporter().options.complianceLevel >> 16;
+ if (sl == 0) sl = cl;
+ if (cl == 0) cl = sl;
+ return Math.min((int)(sl - 44), (int)(cl - 44)) >= 10;
+ }
+
public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) {
if (forEach.elementVariable == null) return false;
@@ -288,6 +298,8 @@ public class PatchVal {
boolean var = isVar(forEach.elementVariable, scope);
if (!(val || var)) return false;
+ if (hasNativeVarSupport(scope)) return false;
+
TypeBinding component = getForEachComponentType(forEach.collection, scope);
if (component == null) return false;
TypeReference replacement = makeType(component, forEach.elementVariable.type, false);
diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java
index bee30922..30c63cf0 100755
--- a/src/eclipseAgent/lombok/launch/PatchFixesHider.java
+++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java
@@ -419,6 +419,7 @@ final class PatchFixesHider {
String className = visitor.getClass().getName();
if (!(className.startsWith("org.eclipse.jdt.internal.corext.fix") || className.startsWith("org.eclipse.jdt.internal.ui.fix"))) return false;
+ if (className.equals("org.eclipse.jdt.internal.corext.fix.VariableDeclarationFixCore$WrittenNamesFinder")) return false;
boolean result = false;
try {
diff --git a/src/installer/lombok/installer/IdeLocation.java b/src/installer/lombok/installer/IdeLocation.java
index 6b9a94c6..7cba1e2a 100644
--- a/src/installer/lombok/installer/IdeLocation.java
+++ b/src/installer/lombok/installer/IdeLocation.java
@@ -64,16 +64,4 @@ public abstract class IdeLocation {
return x == null ? p.getPath() : x;
}
}
-
- private static final String LEGAL_PATH_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/";
- private static final String LEGAL_PATH_CHARS_WINDOWS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,/;'[]{}!@#$^&()-_+= :\\";
- public static String escapePath(String path) {
- StringBuilder out = new StringBuilder();
- String legalChars = OsUtils.getOS() == OsUtils.OS.UNIX ? LEGAL_PATH_CHARS : LEGAL_PATH_CHARS_WINDOWS;
- for (char c : path.toCharArray()) {
- if (legalChars.indexOf(c) == -1) out.append('\\');
- out.append(c);
- }
- return out.toString();
- }
}
diff --git a/src/installer/lombok/installer/eclipse/EclipseProductLocation.java b/src/installer/lombok/installer/eclipse/EclipseProductLocation.java
index 73f98a35..4cfd07f5 100644
--- a/src/installer/lombok/installer/eclipse/EclipseProductLocation.java
+++ b/src/installer/lombok/installer/eclipse/EclipseProductLocation.java
@@ -347,8 +347,10 @@ public final class EclipseProductLocation extends IdeLocation {
pathPrefix = pathToLombokJarPrefix;
}
+ // NB: You may be tempted to escape this, but don't; there is no possibility to escape this, but
+ // eclipse/java reads the string following the colon in 'raw' fashion. Spaces, colons - all works fine.
newContents.append(String.format(
- "-javaagent:%s", escapePath(pathPrefix + "lombok.jar"))).append(OS_NEWLINE);
+ "-javaagent:%s", pathPrefix + "lombok.jar")).append(OS_NEWLINE);
FileOutputStream fos = new FileOutputStream(eclipseIniPath);
try {
diff --git a/src/stubs/com/sun/tools/javac/comp/ArgumentAttr.java b/src/stubs/com/sun/tools/javac/comp/ArgumentAttr.java
new file mode 100644
index 00000000..954a4592
--- /dev/null
+++ b/src/stubs/com/sun/tools/javac/comp/ArgumentAttr.java
@@ -0,0 +1,12 @@
+/*
+ * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
+ */
+package com.sun.tools.javac.comp;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.util.Context;
+
+// JDK9+
+public class ArgumentAttr extends JCTree.Visitor {
+ public static ArgumentAttr instance(Context context) { return null; }
+} \ No newline at end of file
diff --git a/src/stubsstubs/com/sun/tools/javac/tree/JCTree.java b/src/stubsstubs/com/sun/tools/javac/tree/JCTree.java
index 28edf665..3b86bf3d 100644
--- a/src/stubsstubs/com/sun/tools/javac/tree/JCTree.java
+++ b/src/stubsstubs/com/sun/tools/javac/tree/JCTree.java
@@ -2,4 +2,5 @@ package com.sun.tools.javac.tree;
public class JCTree {
public static class JCCompilationUnit extends JCTree {}
+ public static abstract class Visitor {}
}
diff --git a/src/utils/lombok/eclipse/Eclipse.java b/src/utils/lombok/eclipse/Eclipse.java
index 8af481b9..0f42ddc6 100644
--- a/src/utils/lombok/eclipse/Eclipse.java
+++ b/src/utils/lombok/eclipse/Eclipse.java
@@ -224,8 +224,11 @@ public class Eclipse {
int highestVersionSoFar = 0;
for (Field f : ClassFileConstants.class.getDeclaredFields()) {
try {
- if (f.getName().startsWith("JDK1_")) {
- int thisVersion = Integer.parseInt(f.getName().substring("JDK1_".length()));
+ if (f.getName().startsWith("JDK")) {
+ String versionString = f.getName().substring("JDK".length());
+ if (versionString.startsWith("1_")) versionString = versionString.substring("1_".length());
+
+ int thisVersion = Integer.parseInt(versionString);
if (thisVersion > highestVersionSoFar) {
highestVersionSoFar = thisVersion;
latestEcjCompilerVersionConstantCached = (Long) f.get(null);
diff --git a/src/utils/lombok/javac/TreeMirrorMaker.java b/src/utils/lombok/javac/TreeMirrorMaker.java
index 3cb79412..44e26ab6 100644
--- a/src/utils/lombok/javac/TreeMirrorMaker.java
+++ b/src/utils/lombok/javac/TreeMirrorMaker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2015 The Project Lombok Authors.
+ * Copyright (C) 2010-2021 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
@@ -32,8 +32,10 @@ import lombok.javac.JavacTreeMaker.TypeTag;
import com.sun.source.tree.LabeledStatementTree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeCopier;
+import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
@@ -56,14 +58,14 @@ public class TreeMirrorMaker extends TreeCopier<Void> {
@Override public <T extends JCTree> T copy(T original) {
T copy = super.copy(original);
- originalToCopy.put(original, copy);
+ putIfAbsent(originalToCopy, original, copy);
return copy;
}
@Override public <T extends JCTree> T copy(T original, Void p) {
T copy = super.copy(original, p);
- originalToCopy.put(original, copy);
- return copy;
+ putIfAbsent(originalToCopy, original, copy);
+ return copy;
}
@Override public <T extends JCTree> List<T> copy(List<T> originals) {
@@ -71,7 +73,7 @@ public class TreeMirrorMaker extends TreeCopier<Void> {
if (originals != null) {
Iterator<T> it1 = originals.iterator();
Iterator<T> it2 = copies.iterator();
- while (it1.hasNext()) originalToCopy.put(it1.next(), it2.next());
+ while (it1.hasNext()) putIfAbsent(originalToCopy, it1.next(), it2.next());
}
return copies;
}
@@ -81,7 +83,7 @@ public class TreeMirrorMaker extends TreeCopier<Void> {
if (originals != null) {
Iterator<T> it1 = originals.iterator();
Iterator<T> it2 = copies.iterator();
- while (it1.hasNext()) originalToCopy.put(it1.next(), it2.next());
+ while (it1.hasNext()) putIfAbsent(originalToCopy, it1.next(), it2.next());
}
return copies;
}
@@ -109,6 +111,21 @@ public class TreeMirrorMaker extends TreeCopier<Void> {
if (wipeSymAndType) {
copy.sym = null;
copy.type = null;
+ } else {
+ if (original.vartype != null) {
+ copy.vartype.type = original.vartype.type;
+ original.vartype.accept(new TreeScanner() {
+ @Override public void scan(JCTree tree) {
+ super.scan(tree);
+ originalToCopy.get(tree).type = tree.type;
+ }
+
+ @Override public void visitSelect(JCFieldAccess tree) {
+ super.visitSelect(tree);
+ ((JCFieldAccess) originalToCopy.get(tree)).sym = tree.sym;
+ }
+ });
+ }
}
}
@@ -120,4 +137,10 @@ public class TreeMirrorMaker extends TreeCopier<Void> {
@Override public JCTree visitLabeledStatement(LabeledStatementTree node, Void p) {
return node.getStatement().accept(this, p);
}
+
+ private <K, V> void putIfAbsent(Map<K, V> map, K key, V value) {
+ if (!map.containsKey(key)) {
+ map.put(key, value);
+ }
+ }
}
diff --git a/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java b/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java
index f29f501b..e625cd8d 100644
--- a/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java
+++ b/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java
@@ -21,6 +21,7 @@
*/
package lombok.javac.java8;
+import java.nio.Buffer;
import java.nio.CharBuffer;
import com.sun.tools.javac.parser.Scanner;
@@ -79,7 +80,7 @@ public class CommentCollectingScannerFactory extends ScannerFactory {
int limit;
if (input instanceof CharBuffer && ((CharBuffer) input).hasArray()) {
CharBuffer cb = (CharBuffer) input;
- cb.compact().flip();
+ ((Buffer)cb.compact()).flip();
array = cb.array();
limit = cb.limit();
} else {
diff --git a/test/transform/resource/after-delombok/ValAnonymousSubclassSelfReference.java b/test/transform/resource/after-delombok/ValAnonymousSubclassSelfReference.java
new file mode 100644
index 00000000..a1176a3c
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValAnonymousSubclassSelfReference.java
@@ -0,0 +1,17 @@
+import java.util.Map;
+import java.util.HashMap;
+
+public class ValAnonymousSubclassSelfReference {
+ public <T> void test(T arg) {
+ T d = arg;
+ Integer[] e = new Integer[1];
+ int[] f = new int[0];
+ java.util.Map<java.lang.String, Integer> g = new HashMap<String, Integer>();
+ Integer h = 0;
+ int i = 0;
+ final int j = 1;
+ final int k = 2;
+ new ValAnonymousSubclassSelfReference() {
+ };
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValSwitchExpression.java b/test/transform/resource/after-delombok/ValSwitchExpression.java
new file mode 100644
index 00000000..ba26c137
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValSwitchExpression.java
@@ -0,0 +1,11 @@
+// version 14:
+public class ValSwitchExpression {
+ public void method(int arg) {
+ final var x = switch (arg) {
+ default -> {
+ final var s = "string";
+ yield arg;
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValToNative.java b/test/transform/resource/after-delombok/ValToNative.java
new file mode 100644
index 00000000..64aff9e5
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValToNative.java
@@ -0,0 +1,15 @@
+// version 10:
+import java.io.IOException;
+import java.util.Arrays;
+
+public class ValToNative {
+ private void test() throws IOException {
+ final var intField = 1;
+ for (final var s : Arrays.asList("1")) {
+ final var s2 = s;
+ }
+ try (var in = getClass().getResourceAsStream("ValToNative.class")) {
+ final var j = in.read();
+ }
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/ValAnonymousSubclassSelfReference.java b/test/transform/resource/after-ecj/ValAnonymousSubclassSelfReference.java
new file mode 100644
index 00000000..12b0f640
--- /dev/null
+++ b/test/transform/resource/after-ecj/ValAnonymousSubclassSelfReference.java
@@ -0,0 +1,23 @@
+import java.util.Map;
+import java.util.HashMap;
+import lombok.val;
+public class ValAnonymousSubclassSelfReference {
+ public ValAnonymousSubclassSelfReference() {
+ super();
+ }
+ public <T>void test(T arg) {
+ T d = arg;
+ Integer[] e = new Integer[1];
+ int[] f = new int[0];
+ java.util.Map<java.lang.String, Integer> g = new HashMap<String, Integer>();
+ Integer h = 0;
+ int i = 0;
+ final @val int j = 1;
+ final @val int k = 2;
+ new ValAnonymousSubclassSelfReference() {
+ x() {
+ super();
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/ValSwitchExpression.java b/test/transform/resource/after-ecj/ValSwitchExpression.java
new file mode 100644
index 00000000..4e848572
--- /dev/null
+++ b/test/transform/resource/after-ecj/ValSwitchExpression.java
@@ -0,0 +1,16 @@
+// version 14:
+import lombok.val;
+public class ValSwitchExpression {
+ public ValSwitchExpression() {
+ super();
+ }
+ public void method(int arg) {
+ final @val var x = switch (arg) {
+ default ->
+ {
+ final @val var s = "string";
+ yield arg;
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/ValToNative.java b/test/transform/resource/after-ecj/ValToNative.java
new file mode 100644
index 00000000..2c8d721c
--- /dev/null
+++ b/test/transform/resource/after-ecj/ValToNative.java
@@ -0,0 +1,19 @@
+import java.io.IOException;
+import java.util.Arrays;
+import lombok.val;
+public class ValToNative {
+ public ValToNative() {
+ super();
+ }
+ private void test() throws IOException {
+ final @val var intField = 1;
+ for (final @val var s : Arrays.asList("1"))
+ {
+ final @val var s2 = s;
+ }
+ try (final @val var in = getClass().getResourceAsStream("ValToNative.class"))
+ {
+ final @val var j = in.read();
+ }
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/before/MixGetterVal.java b/test/transform/resource/before/MixGetterVal.java
index 3f06b1a8..4568902b 100644
--- a/test/transform/resource/before/MixGetterVal.java
+++ b/test/transform/resource/before/MixGetterVal.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.Getter;
import lombok.val;
diff --git a/test/transform/resource/before/TrickyTypeResolution.java b/test/transform/resource/before/TrickyTypeResolution.java
index 94d97fe0..7f3866ee 100644
--- a/test/transform/resource/before/TrickyTypeResolution.java
+++ b/test/transform/resource/before/TrickyTypeResolution.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.*;
class TrickyDoNothing {
@interface Getter {}
diff --git a/test/transform/resource/before/ValAnonymousSubclassSelfReference.java b/test/transform/resource/before/ValAnonymousSubclassSelfReference.java
new file mode 100644
index 00000000..e7c30c84
--- /dev/null
+++ b/test/transform/resource/before/ValAnonymousSubclassSelfReference.java
@@ -0,0 +1,21 @@
+// issue 2420: to trigger the problem 2 var/val, at least one normal variable and a anonymous self reference is required
+import java.util.Map;
+import java.util.HashMap;
+
+import lombok.val;
+
+public class ValAnonymousSubclassSelfReference {
+ public <T> void test(T arg) {
+ T d = arg;
+ Integer[] e = new Integer[1];
+ int[] f = new int[0];
+ java.util.Map<java.lang.String, Integer> g = new HashMap<String, Integer>();
+ Integer h = 0;
+ int i = 0;
+
+ val j = 1;
+ val k = 2;
+
+ new ValAnonymousSubclassSelfReference() { };
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/before/ValAnonymousSubclassWithGenerics.java b/test/transform/resource/before/ValAnonymousSubclassWithGenerics.java
index c0f8157a..a434ba9d 100644
--- a/test/transform/resource/before/ValAnonymousSubclassWithGenerics.java
+++ b/test/transform/resource/before/ValAnonymousSubclassWithGenerics.java
@@ -1,3 +1,4 @@
+// version :9
// issue 205: val inside anonymous inner classes is a bit tricky in javac, this test ensures we don't break it.
import java.util.*;
import lombok.val;
diff --git a/test/transform/resource/before/ValComplex.java b/test/transform/resource/before/ValComplex.java
index e20124a2..f1898cfd 100644
--- a/test/transform/resource/before/ValComplex.java
+++ b/test/transform/resource/before/ValComplex.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.val;
public class ValComplex {
diff --git a/test/transform/resource/before/ValDefault.java b/test/transform/resource/before/ValDefault.java
index 75124c3c..ded4b074 100644
--- a/test/transform/resource/before/ValDefault.java
+++ b/test/transform/resource/before/ValDefault.java
@@ -1,4 +1,4 @@
-// version 8:
+// version 8:9
interface ValDefault {
int size();
diff --git a/test/transform/resource/before/ValDelegateMethodReference.java b/test/transform/resource/before/ValDelegateMethodReference.java
index 3d1f082c..8cfc2c33 100644
--- a/test/transform/resource/before/ValDelegateMethodReference.java
+++ b/test/transform/resource/before/ValDelegateMethodReference.java
@@ -1,4 +1,4 @@
-//version 8:
+//version 8:9
//platform !eclipse: Requires a 'full' eclipse with intialized workspace, and we don't (yet) have that set up properly in the test run.
import lombok.Getter;
import lombok.Setter;
diff --git a/test/transform/resource/before/ValErrors.java b/test/transform/resource/before/ValErrors.java
index 87383719..290a1f72 100644
--- a/test/transform/resource/before/ValErrors.java
+++ b/test/transform/resource/before/ValErrors.java
@@ -1,3 +1,4 @@
+// version :9
// unchanged
import lombok.val;
diff --git a/test/transform/resource/before/ValFinal.java b/test/transform/resource/before/ValFinal.java
index 3c5af366..293c9bce 100644
--- a/test/transform/resource/before/ValFinal.java
+++ b/test/transform/resource/before/ValFinal.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.val;
public class ValFinal {
public void test() {
diff --git a/test/transform/resource/before/ValInBasicFor.java b/test/transform/resource/before/ValInBasicFor.java
index a109bcd3..b137f0d7 100644
--- a/test/transform/resource/before/ValInBasicFor.java
+++ b/test/transform/resource/before/ValInBasicFor.java
@@ -1,3 +1,4 @@
+// version :9
// unchanged
import lombok.val;
diff --git a/test/transform/resource/before/ValInFor.java b/test/transform/resource/before/ValInFor.java
index 35332b34..f2c50139 100644
--- a/test/transform/resource/before/ValInFor.java
+++ b/test/transform/resource/before/ValInFor.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.val;
public class ValInFor {
diff --git a/test/transform/resource/before/ValInLambda.java b/test/transform/resource/before/ValInLambda.java
index 6750d045..a13c79d2 100644
--- a/test/transform/resource/before/ValInLambda.java
+++ b/test/transform/resource/before/ValInLambda.java
@@ -1,4 +1,4 @@
-// version 8:
+// version 8:9
import java.util.function.Function;
import java.util.function.Supplier;
diff --git a/test/transform/resource/before/ValInMultiDeclaration.java b/test/transform/resource/before/ValInMultiDeclaration.java
index 1c333ebb..0f4e604b 100644
--- a/test/transform/resource/before/ValInMultiDeclaration.java
+++ b/test/transform/resource/before/ValInMultiDeclaration.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.val;
public class ValInMultiDeclaration {
public void test() {
diff --git a/test/transform/resource/before/ValInTryWithResources.java b/test/transform/resource/before/ValInTryWithResources.java
index a7820062..5c885f79 100644
--- a/test/transform/resource/before/ValInTryWithResources.java
+++ b/test/transform/resource/before/ValInTryWithResources.java
@@ -1,4 +1,4 @@
-//version 7:
+//version 7:9
import lombok.val;
import java.io.IOException;
diff --git a/test/transform/resource/before/ValLambda.java b/test/transform/resource/before/ValLambda.java
index e956bcd3..8f55d222 100644
--- a/test/transform/resource/before/ValLambda.java
+++ b/test/transform/resource/before/ValLambda.java
@@ -1,4 +1,4 @@
-// version 8:
+// version 8:9
import java.io.Serializable;
class ValLambda {
diff --git a/test/transform/resource/before/ValLessSimple.java b/test/transform/resource/before/ValLessSimple.java
index b81cc22c..1ed738cc 100644
--- a/test/transform/resource/before/ValLessSimple.java
+++ b/test/transform/resource/before/ValLessSimple.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.val;
public class ValLessSimple {
diff --git a/test/transform/resource/before/ValLub.java b/test/transform/resource/before/ValLub.java
index 509a4f8b..e3b55950 100644
--- a/test/transform/resource/before/ValLub.java
+++ b/test/transform/resource/before/ValLub.java
@@ -1,3 +1,4 @@
+// version :9
class ValLub {
public void easyLub() {
java.util.Map<String, Number> m = java.util.Collections.emptyMap();
diff --git a/test/transform/resource/before/ValNullInit.java b/test/transform/resource/before/ValNullInit.java
index 649bc0cd..c1610af3 100644
--- a/test/transform/resource/before/ValNullInit.java
+++ b/test/transform/resource/before/ValNullInit.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.val;
class ValNullInit {
diff --git a/test/transform/resource/before/ValOutersWithGenerics.java b/test/transform/resource/before/ValOutersWithGenerics.java
index 1b29d37c..99b71735 100644
--- a/test/transform/resource/before/ValOutersWithGenerics.java
+++ b/test/transform/resource/before/ValOutersWithGenerics.java
@@ -1,3 +1,4 @@
+// version :9
import java.util.*;
import lombok.val;
diff --git a/test/transform/resource/before/ValRawType.java b/test/transform/resource/before/ValRawType.java
index 3ef8527e..fa47c536 100644
--- a/test/transform/resource/before/ValRawType.java
+++ b/test/transform/resource/before/ValRawType.java
@@ -1,3 +1,4 @@
+// version :9
import java.util.List;
import lombok.val;
diff --git a/test/transform/resource/before/ValSimple.java b/test/transform/resource/before/ValSimple.java
index 04763be2..5d1911da 100644
--- a/test/transform/resource/before/ValSimple.java
+++ b/test/transform/resource/before/ValSimple.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.val;
public class ValSimple {
diff --git a/test/transform/resource/before/ValSwitchExpression.java b/test/transform/resource/before/ValSwitchExpression.java
new file mode 100644
index 00000000..c3838acc
--- /dev/null
+++ b/test/transform/resource/before/ValSwitchExpression.java
@@ -0,0 +1,13 @@
+// version 14:
+import lombok.val;
+
+public class ValSwitchExpression {
+ public void method(int arg) {
+ val x = switch (arg) {
+ default -> {
+ val s = "string";
+ yield arg;
+ }
+ };
+ }
+}
diff --git a/test/transform/resource/before/ValToNative.java b/test/transform/resource/before/ValToNative.java
new file mode 100644
index 00000000..3b4e6fa8
--- /dev/null
+++ b/test/transform/resource/before/ValToNative.java
@@ -0,0 +1,19 @@
+// version 10:
+import java.io.IOException;
+import java.util.Arrays;
+
+import lombok.val;
+
+public class ValToNative {
+ private void test() throws IOException {
+ val intField = 1;
+
+ for (val s : Arrays.asList("1")) {
+ val s2 = s;
+ }
+
+ try (val in = getClass().getResourceAsStream("ValToNative.class")) {
+ val j = in.read();
+ }
+ }
+}
diff --git a/test/transform/resource/before/ValWeirdTypes.java b/test/transform/resource/before/ValWeirdTypes.java
index f62feca6..710e236b 100644
--- a/test/transform/resource/before/ValWeirdTypes.java
+++ b/test/transform/resource/before/ValWeirdTypes.java
@@ -1,4 +1,4 @@
-// version 8: In java6/7, lub types worked differently, so, the `arraysAsList` method has a slightly different inferred type there.
+// version 8:9 In java6/7, lub types worked differently, so, the `arraysAsList` method has a slightly different inferred type there.
import java.math.BigDecimal;
import java.util.*;
import lombok.val;
diff --git a/test/transform/resource/before/ValWithLabel.java b/test/transform/resource/before/ValWithLabel.java
index f7c3402a..9e15f937 100644
--- a/test/transform/resource/before/ValWithLabel.java
+++ b/test/transform/resource/before/ValWithLabel.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.val;
public class ValWithLabel {
diff --git a/test/transform/resource/before/ValWithLocalClasses.java b/test/transform/resource/before/ValWithLocalClasses.java
index 572a1e7d..0d145aa9 100644
--- a/test/transform/resource/before/ValWithLocalClasses.java
+++ b/test/transform/resource/before/ValWithLocalClasses.java
@@ -1,3 +1,4 @@
+// version :9
//issue 694: In javac, resolving the RHS (which is what val does) can cause an entire class to be resolved, breaking all usage of val inside that class. This tests that we handle that better.
class ValWithLocalClasses1 {
{
diff --git a/test/transform/resource/before/ValWithSelfRefGenerics.java b/test/transform/resource/before/ValWithSelfRefGenerics.java
index d0532606..fdb30d32 100644
--- a/test/transform/resource/before/ValWithSelfRefGenerics.java
+++ b/test/transform/resource/before/ValWithSelfRefGenerics.java
@@ -1,3 +1,4 @@
+// version :9
import lombok.val;
public class ValWithSelfRefGenerics {
public void run(Thing<? extends Comparable<?>> thing, Thing<?> thing2, java.util.List<? extends Number> z) {
diff --git a/website/resources/css/custom.css b/website/resources/css/custom.css
index ded02a06..240d6c4c 100644
--- a/website/resources/css/custom.css
+++ b/website/resources/css/custom.css
@@ -268,6 +268,10 @@ ol.snippet.cmd li:before {
content: ">\A0\A0";
}
+ol.snippet li.continued {
+ padding-left: 2em;
+}
+
.fork-me {
position: fixed;
width: 150px;
diff --git a/website/templates/features/Builder.html b/website/templates/features/Builder.html
index 1b6c6e62..1461161f 100644
--- a/website/templates/features/Builder.html
+++ b/website/templates/features/Builder.html
@@ -21,9 +21,17 @@
<p>
The <code>@Builder</code> annotation produces complex builder APIs for your classes.
</p><p>
- <code>@Builder</code> lets you automatically produce the code required to have your class be instantiable with code such as:<br />
- <code>Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();</code>
- </p><p>
+ <code>@Builder</code> lets you automatically produce the code required to have your class be instantiable with code such as:
+ </p>
+ <ol class="snippet example">
+ <li>Person.builder()</li>
+ <li class="continued">.name("Adam Savage")</li>
+ <li class="continued">.city("San Francisco")</li>
+ <li class="continued">.job("Mythbusters")</li>
+ <li class="continued">.job("Unchained Reaction")</li>
+ <li class="continued">.build();</li>
+ </ol>
+ <p>
<code>@Builder</code> can be placed on a class, or on a constructor, or on a method. While the "on a class" and "on a constructor" mode are the most common use-case, <code>@Builder</code> is most easily explained with the "method" use-case.
</p><p>
A method annotated with <code>@Builder</code> (from now on called the <em>target</em>) causes the following 7 things to be generated:
@@ -46,7 +54,15 @@
</ul>
Each listed generated element will be silently skipped if that element already exists (disregarding parameter counts and looking only at names). This includes the <em>builder</em> itself: If that class already exists, lombok will simply start injecting fields and methods inside this already existing class, unless of course the fields / methods to be injected already exist. You may not put any other method (or constructor) generating lombok annotation on a builder class though; for example, you can not put <code>@EqualsAndHashCode</code> on the builder class.
</p><p>
- <code>@Builder</code> can generate so-called 'singular' methods for collection parameters/fields. These take 1 element instead of an entire list, and add the element to the list. For example: <code>Person.builder().job("Mythbusters").job("Unchained Reaction").build();</code> would result in the <code>List&lt;String&gt; jobs</code> field to have 2 strings in it. To get this behavior, the field/parameter needs to be annotated with <code>@Singular</code>. The feature has <a href="#singular">its own documentation</a>.
+ <code>@Builder</code> can generate so-called 'singular' methods for collection parameters/fields. These take 1 element instead of an entire list, and add the element to the list. For example:
+ </p>
+ <ol class="snippet example">
+ <li>Person.builder()</li>
+ <li class="continued">.job("Mythbusters")</li>
+ <li class="continued">.job("Unchained Reaction")</li>
+ <li class="continued">.build();</li>
+ </ol>
+ <p>would result in the <code>List&lt;String&gt; jobs</code> field to have 2 strings in it. To get this behavior, the field/parameter needs to be annotated with <code>@Singular</code>. The feature has <a href="#singular">its own documentation</a>.
</p><p>
Now that the "method" mode is clear, putting a <code>@Builder</code> annotation on a constructor functions similarly; effectively, constructors are just static methods that have a special syntax to invoke them: Their 'return type' is the class they construct, and their type parameters are the same as the type parameters of the class itself.
</p><p>
diff --git a/website/templates/features/val.html b/website/templates/features/val.html
index 32a8ffdf..1b137c65 100644
--- a/website/templates/features/val.html
+++ b/website/templates/features/val.html
@@ -5,6 +5,9 @@
<p>
<code>val</code> was introduced in lombok 0.10.
</p>
+ <p>
+ <em>NEW in Lombok 1.18.22: </em><code>val</code> gets replaced with <code>final var</code>.
+ </p>
</@f.history>
<@f.overview>
<p>