Skip to content

Commit

Permalink
Merge branch 'projectlombok:master' into feature/builder-setterPrefix…
Browse files Browse the repository at this point in the history
…-configuration
  • Loading branch information
sadv1r committed Jul 10, 2021
2 parents 384aec3 + e050daf commit c5f39ec
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 19 deletions.
4 changes: 2 additions & 2 deletions src/core/lombok/SneakyThrows.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@
* 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) {
Expand Down
2 changes: 1 addition & 1 deletion src/core/lombok/eclipse/handlers/HandleLog.java
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ public static class HandleCustomLog extends EclipseAnnotationHandler<lombok.Cust
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);
Expand Down
60 changes: 55 additions & 5 deletions src/core/lombok/javac/JavacResolution.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -29,6 +29,7 @@
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;

Expand All @@ -45,6 +46,7 @@
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;
Expand All @@ -68,6 +70,7 @@
import lombok.permit.Permit;

public class JavacResolution {
private final Context context;
private final Attr attr;
private final CompilerMessageSuppressor messageSuppressor;

Expand All @@ -82,6 +85,7 @@ public class JavacResolution {
}

public JavacResolution(Context context) {
this.context = context;
attr = Attr.instance(context);
messageSuppressor = new CompilerMessageSuppressor(context);
}
Expand Down Expand Up @@ -245,10 +249,19 @@ private void attrib(JCTree tree, Env<AttrContext> env) {
} 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 {
Expand Down Expand Up @@ -283,6 +296,43 @@ public static Type Types_upperBound(Types types, Type type) {
}
}

/**
* 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());
Expand Down
2 changes: 1 addition & 1 deletion src/core/lombok/javac/handlers/HandleLog.java
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ public static class HandleCustomLog extends JavacAnnotationHandler<lombok.Custom
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);
Expand Down
12 changes: 12 additions & 0 deletions src/stubs/com/sun/tools/javac/comp/ArgumentAttr.java
Original file line number Diff line number Diff line change
@@ -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; }
}
1 change: 1 addition & 0 deletions src/stubsstubs/com/sun/tools/javac/tree/JCTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

public class JCTree {
public static class JCCompilationUnit extends JCTree {}
public static abstract class Visitor {}
}
18 changes: 12 additions & 6 deletions src/utils/lombok/javac/TreeMirrorMaker.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -56,22 +56,22 @@ public TreeMirrorMaker(JavacTreeMaker maker, Context context) {

@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) {
List<T> copies = super.copy(originals);
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;
}
Expand All @@ -81,7 +81,7 @@ public TreeMirrorMaker(JavacTreeMaker maker, Context context) {
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;
}
Expand Down Expand Up @@ -120,4 +120,10 @@ public Map<JCTree, JCTree> getOriginalToCopyMap() {
@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);
}
}
}
11 changes: 11 additions & 0 deletions test/transform/resource/after-delombok/ValSwitchExpression.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// version 14:
public class ValSwitchExpression {
public void method(int arg) {
final int x = switch (arg) {
default -> {
final java.lang.String s = "string";
yield arg;
}
};
}
}
16 changes: 16 additions & 0 deletions test/transform/resource/after-ecj/ValSwitchExpression.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// version 14:
import lombok.val;
public class ValSwitchExpression {
public ValSwitchExpression() {
super();
}
public void method(int arg) {
final @val int x = switch (arg) {
default ->
{
final @val java.lang.String s = "string";
yield arg;
}
};
}
}
13 changes: 13 additions & 0 deletions test/transform/resource/before/ValSwitchExpression.java
Original file line number Diff line number Diff line change
@@ -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;
}
};
}
}
4 changes: 4 additions & 0 deletions website/resources/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
24 changes: 20 additions & 4 deletions website/templates/features/Builder.html
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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>
Expand Down

0 comments on commit c5f39ec

Please sign in to comment.