diff options
41 files changed, 471 insertions, 168 deletions
@@ -950,7 +950,7 @@ You can also create your own by writing a 'testenvironment.properties' file. The <ant antfile="buildScripts/website.ant.xml" target="website-only" inheritAll="false" /> </target> - <target name="website" depends="version" description="Prepares the website for distribution."> + <target name="website" depends="version, compile" description="Prepares the website for distribution."> <ant antfile="buildScripts/website.ant.xml" target="website" inheritAll="false"> <property name="lombok.version" value="${lombok.version}" /> <property name="lombok.fullversion" value="${lombok.fullversion}" /> @@ -975,7 +975,7 @@ You can also create your own by writing a 'testenvironment.properties' file. The </ant> </target> - <target name="website-publish" depends="config-ssh, clean, version, ensureSupportersDeps" + <target name="website-publish" depends="config-ssh, clean, version, ensureSupportersDeps, compile" description="Prepares the website for distribution and then publishes it to projectlombok.org."> <ant antfile="buildScripts/website.ant.xml" target="website-publish" inheritAll="false"> <property name="lombok.version" value="${lombok.version}" /> diff --git a/buildScripts/ivy-repo/org.projectlombok-lombok.patcher-0.30.xml b/buildScripts/ivy-repo/org.projectlombok-lombok.patcher-0.34.xml index 9a87a8bf..778462cf 100644 --- a/buildScripts/ivy-repo/org.projectlombok-lombok.patcher-0.30.xml +++ b/buildScripts/ivy-repo/org.projectlombok-lombok.patcher-0.34.xml @@ -1,5 +1,5 @@ <ivy-module version="2.0"> - <info organisation="org.projectlombok" module="lombok.patcher" revision="0.30" publication="20180910222000"> + <info organisation="org.projectlombok" module="lombok.patcher" revision="0.34" publication="20190925010000"> <license name="MIT License" url="https://www.opensource.org/licenses/mit-license.php" /> <ivyauthor name="rzwitserloot" url="https://github.com/rzwitserloot" /> <ivyauthor name="rspilker" url="https://github.com/rspilker" /> @@ -9,6 +9,6 @@ <conf name="default" /> </configurations> <publications> - <artifact conf="default" url="https://projectlombok.org/downloads/lombok.patcher-0.30.jar" /> + <artifact conf="default" url="https://projectlombok.org/downloads/lombok.patcher-0.34.jar" /> </publications> </ivy-module> diff --git a/buildScripts/ivy.xml b/buildScripts/ivy.xml index bc4cc0e6..1edce11d 100644 --- a/buildScripts/ivy.xml +++ b/buildScripts/ivy.xml @@ -18,7 +18,7 @@ <conf name="supporters" /> </configurations> <dependencies> - <dependency org="org.projectlombok" name="lombok.patcher" rev="0.32" conf="buildBase->default; runtime->default" /> + <dependency org="org.projectlombok" name="lombok.patcher" rev="0.36" conf="buildBase->default; runtime->default" /> <dependency org="zwitserloot.com" name="cmdreader" rev="1.2" conf="buildBase->runtime; runtime" /> <dependency org="junit" name="junit" rev="4.8.2" conf="test->default; contrib->sources" /> diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 818ced1a..44b7f553 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -1,8 +1,9 @@ Lombok Changelog ---------------- -### v1.18.9 "Edgy Guinea Pig" -* No changes yet. +### v1.18.11 "Edgy Guinea Pig" +* PLATFORM: Support for JDK13 (including `yield` in switch expressions, as well as delombok having a nicer style for arrow-style switch blocks, and text blocks). + ### v1.18.10 (September 10th, 2019) * PROMOTION: `@Wither` has been promoted to the main package, renamed to `@With`. Otherwise, no changes have been made to the annotation. The old experimental annotation will remain for a few versions as a deprecated annotation. If you had `lombok.config` configuration for this annotation, the configuration keys for this feature have been renamed. diff --git a/src/core/lombok/bytecode/ClassFileMetaData.java b/src/core/lombok/bytecode/ClassFileMetaData.java index 826eed83..0510292d 100644 --- a/src/core/lombok/bytecode/ClassFileMetaData.java +++ b/src/core/lombok/bytecode/ClassFileMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2018 The Project Lombok Authors. + * Copyright (C) 2010-2019 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 @@ -43,6 +43,7 @@ public class ClassFileMetaData { // New in java7: support for methodhandles and invokedynamic private static final byte METHOD_HANDLE = 15; private static final byte METHOD_TYPE = 16; + private static final byte DYNAMIC = 17; private static final byte INVOKE_DYNAMIC = 18; // New in java9: support for modules private static final byte MODULE = 19; @@ -96,6 +97,7 @@ public class ClassFileMetaData { case INTERFACE_METHOD: case NAME_TYPE: case INVOKE_DYNAMIC: + case DYNAMIC: position += 4; break; case LONG: @@ -388,6 +390,9 @@ public class ClassFileMetaData { case METHOD_TYPE: result.append("MethodType..."); break; + case DYNAMIC: + result.append("Dynamic..."); + break; case INVOKE_DYNAMIC: result.append("InvokeDynamic..."); break; diff --git a/src/core/lombok/core/LombokInternalAliasing.java b/src/core/lombok/core/LombokInternalAliasing.java index 68ced84f..60dea0f2 100644 --- a/src/core/lombok/core/LombokInternalAliasing.java +++ b/src/core/lombok/core/LombokInternalAliasing.java @@ -28,8 +28,6 @@ import java.util.HashMap; import java.util.Map; public class LombokInternalAliasing { - /** Maps a package name to a space separated list of packages. If the key package is star-imported, assume all packages in the 'value' part of the MapEntry are too. */ - public static final Map<String, Collection<String>> IMPLIED_EXTRA_STAR_IMPORTS; public static final Map<String, String> ALIASES; public static final Map<String, Collection<String>> REVERSE_ALIASES; @@ -43,36 +41,31 @@ public class LombokInternalAliasing { } static { - Map<String, Collection<String>> m1 = new HashMap<String, Collection<String>>(); - m1.put("lombok.experimental", Collections.singleton("lombok")); - m1.put("lombok", Collections.singleton("lombok.experimental")); - IMPLIED_EXTRA_STAR_IMPORTS = Collections.unmodifiableMap(m1); + Map<String, String> m1 = new HashMap<String, String>(); + m1.put("lombok.experimental.Value", "lombok.Value"); + m1.put("lombok.experimental.Builder", "lombok.Builder"); + m1.put("lombok.experimental.var", "lombok.var"); + m1.put("lombok.Delegate", "lombok.experimental.Delegate"); + m1.put("lombok.experimental.Wither", "lombok.With"); + ALIASES = Collections.unmodifiableMap(m1); - Map<String, String> m2 = new HashMap<String, String>(); - m2.put("lombok.experimental.Value", "lombok.Value"); - m2.put("lombok.experimental.Builder", "lombok.Builder"); - m2.put("lombok.experimental.var", "lombok.var"); - m2.put("lombok.Delegate", "lombok.experimental.Delegate"); - m2.put("lombok.experimental.Wither", "lombok.With"); - ALIASES = Collections.unmodifiableMap(m2); - - Map<String, Collection<String>> m3 = new HashMap<String, Collection<String>>(); - for (Map.Entry<String, String> e : m2.entrySet()) { - Collection<String> c = m3.get(e.getValue()); + Map<String, Collection<String>> m2 = new HashMap<String, Collection<String>>(); + for (Map.Entry<String, String> e : m1.entrySet()) { + Collection<String> c = m2.get(e.getValue()); if (c == null) { - m3.put(e.getValue(), Collections.singleton(e.getKey())); + m2.put(e.getValue(), Collections.singleton(e.getKey())); } else if (c.size() == 1) { Collection<String> newC = new ArrayList<String>(2); newC.addAll(c); - m3.put(e.getValue(), c); + m2.put(e.getValue(), c); } else { c.add(e.getKey()); } } - for (Map.Entry<String, Collection<String>> e : m3.entrySet()) { + for (Map.Entry<String, Collection<String>> e : m2.entrySet()) { Collection<String> c = e.getValue(); if (c.size() > 1) e.setValue(Collections.unmodifiableList((ArrayList<String>) c)); } - REVERSE_ALIASES = Collections.unmodifiableMap(m3); + REVERSE_ALIASES = Collections.unmodifiableMap(m2); } } diff --git a/src/core/lombok/core/TypeLibrary.java b/src/core/lombok/core/TypeLibrary.java index 113ce67e..8b9fa4b0 100644 --- a/src/core/lombok/core/TypeLibrary.java +++ b/src/core/lombok/core/TypeLibrary.java @@ -21,8 +21,11 @@ */ package lombok.core; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -36,18 +39,18 @@ import java.util.Map; * <ul><li>foo.Spork</li><li>Spork</li><li>foo.*</li></ul> */ public class TypeLibrary { - private final Map<String, String> unqualifiedToQualifiedMap; + private final Map<String, Object> unqualifiedToQualifiedMap; // maps to usually a string, but could be a string array in aliasing cases. private final String unqualified, qualified; private boolean locked; public TypeLibrary() { - unqualifiedToQualifiedMap = new HashMap<String, String>(); + unqualifiedToQualifiedMap = new HashMap<String, Object>(); unqualified = null; qualified = null; } public TypeLibrary(TypeLibrary parent) { - unqualifiedToQualifiedMap = new HashMap<String, String>(); + unqualifiedToQualifiedMap = new HashMap<String, Object>(); unqualified = null; qualified = null; } @@ -58,7 +61,7 @@ public class TypeLibrary { private TypeLibrary(String fqnSingleton) { if (fqnSingleton.indexOf("$") != -1) { - unqualifiedToQualifiedMap = new HashMap<String, String>(); + unqualifiedToQualifiedMap = new HashMap<String, Object>(); unqualified = null; qualified = null; addType(fqnSingleton); @@ -93,6 +96,9 @@ public class TypeLibrary { * @param fullyQualifiedTypeName the FQN type name, such as 'java.lang.String'. */ public void addType(String fullyQualifiedTypeName) { + Collection<String> oldNames = LombokInternalAliasing.REVERSE_ALIASES.get(fullyQualifiedTypeName); + if (oldNames != null) for (String oldName : oldNames) addType(oldName); + String dotBased = fullyQualifiedTypeName.replace("$", "."); if (locked) throw new IllegalStateException("locked"); @@ -102,22 +108,16 @@ public class TypeLibrary { String unqualified = fullyQualifiedTypeName.substring(idx + 1); if (unqualifiedToQualifiedMap == null) throw new IllegalStateException("SingleType library"); - unqualifiedToQualifiedMap.put(unqualified.replace("$", "."), dotBased); - unqualifiedToQualifiedMap.put(unqualified, dotBased); - unqualifiedToQualifiedMap.put(fullyQualifiedTypeName, dotBased); - unqualifiedToQualifiedMap.put(dotBased, dotBased); - Collection<String> oldNames = LombokInternalAliasing.REVERSE_ALIASES.get(fullyQualifiedTypeName); - if (oldNames != null) for (String oldName : oldNames) { - unqualifiedToQualifiedMap.put(oldName, dotBased); - int li = oldName.lastIndexOf('.'); - if (li != -1) unqualifiedToQualifiedMap.put(oldName.substring(li + 1), dotBased); - } + put(unqualified.replace("$", "."), dotBased); + put(unqualified, dotBased); + put(fullyQualifiedTypeName, dotBased); + put(dotBased, dotBased); int idx2 = fullyQualifiedTypeName.indexOf('$', idx + 1); while (idx2 != -1) { String unq = fullyQualifiedTypeName.substring(idx2 + 1); - unqualifiedToQualifiedMap.put(unq.replace("$", "."), dotBased); - unqualifiedToQualifiedMap.put(unq, dotBased); + put(unq.replace("$", "."), dotBased); + put(unq, dotBased); idx2 = fullyQualifiedTypeName.indexOf('$', idx2 + 1); } } @@ -126,13 +126,33 @@ public class TypeLibrary { * Translates an unqualified name such as 'String' to 'java.lang.String', _if_ you added 'java.lang.String' to the library via the {@code addType} method. * Also returns the input if it is equal to a fully qualified name added to this type library. * - * Returns null if it does not match any type in this type library. + * Returns an empty collection if it does not match any type in this type library. */ - public String toQualified(String typeReference) { + public List<String> toQualifieds(String typeReference) { if (unqualifiedToQualifiedMap == null) { - if (typeReference.equals(unqualified) || typeReference.equals(qualified)) return qualified; + if (typeReference.equals(unqualified) || typeReference.equals(qualified)) return Collections.singletonList(qualified); return null; } - return unqualifiedToQualifiedMap.get(typeReference); + + Object v = unqualifiedToQualifiedMap.get(typeReference); + if (v == null) return Collections.emptyList(); + if (v instanceof String) return Collections.singletonList((String) v); + return Arrays.asList((String[]) v); + } + + private void put(String k, String v) { + Object old = unqualifiedToQualifiedMap.put(k, v); + if (old == null) return; + String[] nv; + if (old instanceof String) { + if (old.equals(v)) return; + nv = new String[] {(String) old, v}; + } else { + String[] s = (String[]) old; + nv = new String[s.length + 1]; + System.arraycopy(s, 0, nv, 0, s.length); + nv[s.length] = v; + } + unqualifiedToQualifiedMap.put(k, nv); } } diff --git a/src/core/lombok/core/TypeResolver.java b/src/core/lombok/core/TypeResolver.java index 06c91138..2c36d1fc 100644 --- a/src/core/lombok/core/TypeResolver.java +++ b/src/core/lombok/core/TypeResolver.java @@ -21,6 +21,8 @@ */ package lombok.core; +import java.util.List; + import lombok.core.AST.Kind; /** @@ -44,13 +46,12 @@ public class TypeResolver { } public String typeRefToFullyQualifiedName(LombokNode<?, ?, ?> context, TypeLibrary library, String typeRef) { - typeRef = LombokInternalAliasing.processAliases(typeRef); // When asking if 'Foo' could possibly be referring to 'bar.Baz', the answer is obviously no. - String qualified = library.toQualified(typeRef); - if (qualified == null) return null; + List<String> qualifieds = library.toQualifieds(typeRef); + if (qualifieds == null || qualifieds.isEmpty()) return null; // When asking if 'lombok.Getter' could possibly be referring to 'lombok.Getter', the answer is obviously yes. - if (typeRef.equals(qualified)) return typeRef; + if (qualifieds.contains(typeRef)) return LombokInternalAliasing.processAliases(typeRef); // When asking if 'Getter' could possibly be referring to 'lombok.Getter' if 'import lombok.Getter;' is in the source file, the answer is yes. int firstDot = typeRef.indexOf('.'); @@ -58,50 +59,58 @@ public class TypeResolver { String firstTypeRef = typeRef.substring(0, firstDot); String fromExplicitImport = imports.getFullyQualifiedNameForSimpleName(firstTypeRef); if (fromExplicitImport != null) { + String fqn = fromExplicitImport + typeRef.substring(firstDot); + if (qualifieds.contains(fqn)) return LombokInternalAliasing.processAliases(fqn); // ... and if 'import foobar.Getter;' is in the source file, the answer is no. - return (fromExplicitImport + typeRef.substring(firstDot)).equals(qualified) ? qualified : null; + return null; } // When asking if 'Getter' could possibly be referring to 'lombok.Getter' and 'import lombok.*; / package lombok;' isn't in the source file. the answer is no. - String pkgName = qualified.substring(0, qualified.length() - typeRef.length() - 1); - if (!imports.hasStarImport(pkgName)) return null; - - // Now the hard part: Given that there is a star import, 'Getter' most likely refers to 'lombok.Getter', but type shadowing may occur in which case it doesn't. - LombokNode<?, ?, ?> n = context; - - mainLoop: - while (n != null) { - if (n.getKind() == Kind.TYPE && firstTypeRef.equals(n.getName())) { - // Our own class or one of our outer classes is named 'typeRef' so that's what 'typeRef' is referring to, not one of our type library classes. - return null; - } + for (String qualified : qualifieds) { + String pkgName = qualified.substring(0, qualified.length() - typeRef.length() - 1); + if (!imports.hasStarImport(pkgName)) continue; - if (n.getKind() == Kind.STATEMENT || n.getKind() == Kind.LOCAL) { - LombokNode<?, ?, ?> newN = n.directUp(); - if (newN == null) break mainLoop; + // Now the hard part: Given that there is a star import, 'Getter' most likely refers to 'lombok.Getter', but type shadowing may occur in which case it doesn't. + LombokNode<?, ?, ?> n = context; + + mainLoop: + while (n != null) { + if (n.getKind() == Kind.TYPE && firstTypeRef.equals(n.getName())) { + // Our own class or one of our outer classes is named 'typeRef' so that's what 'typeRef' is referring to, not one of our type library classes. + return null; + } - if (newN.getKind() == Kind.STATEMENT || newN.getKind() == Kind.INITIALIZER || newN.getKind() == Kind.METHOD) { - for (LombokNode<?, ?, ?> child : newN.down()) { - // We found a method local with the same name above our code. That's the one 'typeRef' is referring to, not - // anything in the type library we're trying to find, so, no matches. - if (child.getKind() == Kind.TYPE && firstTypeRef.equals(child.getName())) return null; - if (child == n) break; + if (n.getKind() == Kind.STATEMENT || n.getKind() == Kind.LOCAL) { + LombokNode<?, ?, ?> newN = n.directUp(); + if (newN == null) break mainLoop; + + if (newN.getKind() == Kind.STATEMENT || newN.getKind() == Kind.INITIALIZER || newN.getKind() == Kind.METHOD) { + for (LombokNode<?, ?, ?> child : newN.down()) { + // We found a method local with the same name above our code. That's the one 'typeRef' is referring to, not + // anything in the type library we're trying to find, so, no matches. + if (child.getKind() == Kind.TYPE && firstTypeRef.equals(child.getName())) return null; + if (child == n) break; + } } + n = newN; + continue mainLoop; } - n = newN; - continue mainLoop; - } - - if (n.getKind() == Kind.TYPE || n.getKind() == Kind.COMPILATION_UNIT) { - for (LombokNode<?, ?, ?> child : n.down()) { - // Inner class that's visible to us has 'typeRef' as name, so that's the one being referred to, not one of our type library classes. - if (child.getKind() == Kind.TYPE && firstTypeRef.equals(child.getName())) return null; + + if (n.getKind() == Kind.TYPE || n.getKind() == Kind.COMPILATION_UNIT) { + for (LombokNode<?, ?, ?> child : n.down()) { + // Inner class that's visible to us has 'typeRef' as name, so that's the one being referred to, not one of our type library classes. + if (child.getKind() == Kind.TYPE && firstTypeRef.equals(child.getName())) return null; + } } + + n = n.directUp(); } - n = n.directUp(); + // If no shadowing thing has been found, the star import 'wins', so, return that. + return LombokInternalAliasing.processAliases(qualified); } - return qualified; + // No star import matches either. + return null; } } diff --git a/src/core/lombok/eclipse/EclipseImportList.java b/src/core/lombok/eclipse/EclipseImportList.java index 47167ec6..6d60f5aa 100644 --- a/src/core/lombok/eclipse/EclipseImportList.java +++ b/src/core/lombok/eclipse/EclipseImportList.java @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import lombok.core.ImportList; import lombok.core.LombokInternalAliasing; @@ -65,19 +64,10 @@ public class EclipseImportList implements ImportList { if (isEqual(packageName, pkg)) return true; if ("java.lang".equals(packageName)) return true; - if (pkg != null && pkg.tokens != null && pkg.tokens.length == 0) { - for (Map.Entry<String, Collection<String>> e : LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.entrySet()) { - if (isEqual(e.getKey(), pkg) && e.getValue().contains(packageName)) return true; - } - } - if (imports != null) for (ImportReference imp : imports) { if ((imp.bits & ASTNode.OnDemand) == 0) continue; if (imp.isStatic()) continue; if (isEqual(packageName, imp)) return true; - for (Map.Entry<String, Collection<String>> e : LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.entrySet()) { - if (isEqual(e.getKey(), imp) && e.getValue().contains(packageName)) return true; - } } return false; diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index 0f463ae2..ce5a1b4c 100755 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java @@ -109,7 +109,9 @@ public class EclipseSingularsRecipes { } public String toQualified(String typeReference) { - return singularizableTypes.toQualified(typeReference); + List<String> q = singularizableTypes.toQualifieds(typeReference); + if (q.isEmpty()) return null; + return q.get(0); } public EclipseSingularizer getSingularizer(String fqn) { diff --git a/src/core/lombok/javac/JavacImportList.java b/src/core/lombok/javac/JavacImportList.java index 468d8c7b..8de61afc 100644 --- a/src/core/lombok/javac/JavacImportList.java +++ b/src/core/lombok/javac/JavacImportList.java @@ -60,11 +60,6 @@ public class JavacImportList implements ImportList { if (pkgStr != null && pkgStr.equals(packageName)) return true; if ("java.lang".equals(packageName)) return true; - if (pkgStr != null) { - Collection<String> extra = LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.get(pkgStr); - if (extra != null && extra.contains(packageName)) return true; - } - for (JCTree def : defs) { if (!(def instanceof JCImport)) continue; if (((JCImport) def).staticImport) continue; @@ -74,8 +69,6 @@ public class JavacImportList implements ImportList { if (!"*".equals(simpleName)) continue; String starImport = ((JCFieldAccess) qual).selected.toString(); if (packageName.equals(starImport)) return true; - Collection<String> extra = LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.get(starImport); - if (extra != null && extra.contains(packageName)) return true; } return false; diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 10e6f9b4..87081dde 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -101,7 +101,9 @@ public class JavacSingularsRecipes { } public String toQualified(String typeReference) { - return singularizableTypes.toQualified(typeReference); + java.util.List<String> q = singularizableTypes.toQualifieds(typeReference); + if (q.isEmpty()) return null; + return q.get(0); } public JavacSingularizer getSingularizer(String fqn, JavacNode node) { diff --git a/src/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java index 76b2715a..7318a8ce 100755 --- a/src/delombok/lombok/delombok/Delombok.java +++ b/src/delombok/lombok/delombok/Delombok.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2018 The Project Lombok Authors. + * Copyright (C) 2009-2019 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 @@ -700,11 +700,12 @@ public class Delombok { String[] argv = argsList.toArray(new String[0]); args.init("javac", argv); options.put("diags.legacy", "TRUE"); + options.put("allowStringFolding", "FALSE"); } else { if (modulepath != null && !modulepath.isEmpty()) throw new IllegalStateException("DELOMBOK: Option --module-path requires usage of JDK9 or higher."); } - CommentCatcher catcher = CommentCatcher.create(context); + CommentCatcher catcher = CommentCatcher.create(context, Javac.getJavaCompilerVersion() >= 13); JavaCompiler compiler = catcher.getCompiler(); List<JCCompilationUnit> roots = new ArrayList<JCCompilationUnit>(); @@ -769,7 +770,7 @@ public class Delombok { FormatPreferences fps = new FormatPreferences(formatPrefs); for (JCCompilationUnit unit : roots) { - DelombokResult result = new DelombokResult(catcher.getComments(unit), unit, force || options.isChanged(unit), fps); + DelombokResult result = new DelombokResult(catcher.getComments(unit), catcher.getTextBlockStarts(unit), unit, force || options.isChanged(unit), fps); if (onlyChanged && !result.isChanged() && !options.isChanged(unit)) { if (verbose) feedback.printf("File: %s [%s]\n", unit.sourcefile.getName(), "unchanged (skipped)"); continue; diff --git a/src/delombok/lombok/delombok/DelombokResult.java b/src/delombok/lombok/delombok/DelombokResult.java index 8985b257..bc19c74d 100644 --- a/src/delombok/lombok/delombok/DelombokResult.java +++ b/src/delombok/lombok/delombok/DelombokResult.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2019 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 @@ -34,12 +34,14 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; public class DelombokResult { private final List<CommentInfo> comments; + private final List<Integer> textBlockStarts; private final JCCompilationUnit compilationUnit; private final boolean changed; private final FormatPreferences formatPreferences; - public DelombokResult(List<CommentInfo> comments, JCCompilationUnit compilationUnit, boolean changed, FormatPreferences formatPreferences) { + public DelombokResult(List<CommentInfo> comments, List<Integer> textBlockStarts, JCCompilationUnit compilationUnit, boolean changed, FormatPreferences formatPreferences) { this.comments = comments; + this.textBlockStarts = textBlockStarts; this.compilationUnit = compilationUnit; this.changed = changed; this.formatPreferences = formatPreferences; @@ -61,12 +63,15 @@ public class DelombokResult { } com.sun.tools.javac.util.List<CommentInfo> comments_; + int[] textBlockStarts_; if (comments instanceof com.sun.tools.javac.util.List) comments_ = (com.sun.tools.javac.util.List<CommentInfo>) comments; else comments_ = com.sun.tools.javac.util.List.from(comments.toArray(new CommentInfo[0])); - + textBlockStarts_ = new int[textBlockStarts.size()]; + int idx = 0; + for (int tbs : textBlockStarts) textBlockStarts_[idx++] = tbs; FormatPreferences preferences = new FormatPreferenceScanner().scan(formatPreferences, getContent()); //compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments_, preferences)); - compilationUnit.accept(new PrettyPrinter(out, compilationUnit, comments_, preferences)); + compilationUnit.accept(new PrettyPrinter(out, compilationUnit, comments_, textBlockStarts_, preferences)); } private CharSequence getContent() throws IOException { diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java index 1532319f..fc5eaec2 100644 --- a/src/delombok/lombok/delombok/PrettyPrinter.java +++ b/src/delombok/lombok/delombok/PrettyPrinter.java @@ -31,6 +31,7 @@ import java.io.Writer; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -153,6 +154,7 @@ public class PrettyPrinter extends JCTree.Visitor { private final Writer out; private final JCCompilationUnit compilationUnit; private List<CommentInfo> comments; + private final int[] textBlockStarts; private final FormatPreferences formatPreferences; private final Map<JCTree, String> docComments; @@ -160,9 +162,10 @@ public class PrettyPrinter extends JCTree.Visitor { private int indent = 0; @SuppressWarnings({"unchecked", "rawtypes"}) - public PrettyPrinter(Writer out, JCCompilationUnit cu, List<CommentInfo> comments, FormatPreferences preferences) { + public PrettyPrinter(Writer out, JCCompilationUnit cu, List<CommentInfo> comments, int[] textBlockStarts, FormatPreferences preferences) { this.out = out; this.comments = comments; + this.textBlockStarts = textBlockStarts; this.compilationUnit = cu; this.formatPreferences = preferences; @@ -488,6 +491,19 @@ public class PrettyPrinter extends JCTree.Visitor { } @Override public void visitImport(JCImport tree) { + if (tree.qualid instanceof JCFieldAccess) { + JCFieldAccess fa = ((JCFieldAccess) tree.qualid); + if (fa.name.length() == 1 && fa.name.contentEquals("*")) { + if (fa.selected instanceof JCFieldAccess) { + JCFieldAccess lombokExperimental = (JCFieldAccess) fa.selected; + if (lombokExperimental.name.contentEquals("experimental") && lombokExperimental.selected instanceof JCIdent && ((JCIdent) lombokExperimental.selected).name.contentEquals("lombok")) { + // do not ever print lombok.experimental.*. + return; + } + } + } + } + aPrint("import "); if (tree.staticImport) print("static "); print(tree.qualid); @@ -727,7 +743,37 @@ public class PrettyPrinter extends JCTree.Visitor { } else if (CTC_BOOLEAN.equals(typeTag)) print(((Number)tree.value).intValue() == 1 ? "true" : "false"); else if (CTC_BOT.equals(typeTag)) print("null"); - else print("\"" + quoteChars(tree.value.toString()) + "\""); + else { + if (Arrays.binarySearch(textBlockStarts, tree.pos) < 0) { + print("\"" + quoteChars(tree.value.toString()) + "\""); + } else { + printTextBlock(tree.value.toString()); + } + } + } + + private void printTextBlock(String s) { + println("\"\"\""); + needsAlign = true; + indent++; + StringBuilder sb = new StringBuilder(); + boolean lineStart = true; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c != ' ' && c != '\t') lineStart = false; + if (c == '\n') { + println(sb); + sb.setLength(0); + needsAlign = true; + lineStart = true; + continue; + } + if (c == '\t' && lineStart) sb.append("\t"); + else sb.append(quoteChar(s.charAt(i))); + } + print(sb); + print("\"\"\""); + indent--; } @Override public void visitMethodDef(JCMethodDecl tree) { @@ -1266,6 +1312,11 @@ public class PrettyPrinter extends JCTree.Visitor { print(";"); needsNewLine = true; needsAlign = true; + } else if (tree.stats.head.getClass().getSimpleName().equals("JCYield")) { + print((JCExpression) readObject(tree.stats.head, "value", null)); + print(";"); + needsNewLine = true; + needsAlign = true; } else { print(tree.stats.head); if (tree.stats.head instanceof JCBlock) needsNewLine = false; @@ -1295,7 +1346,10 @@ public class PrettyPrinter extends JCTree.Visitor { print(")"); } println(" {"); + boolean ruleStyle = isCaseRuleStyle(tree.cases.head); + if (ruleStyle) indent++; print(tree.cases, ""); + if (ruleStyle) indent--; aPrintln("}", tree); } @@ -1311,10 +1365,20 @@ public class PrettyPrinter extends JCTree.Visitor { } println(" {"); List<JCCase> cases = readObject(tree, "cases", null); + boolean ruleStyle = isCaseRuleStyle(cases.head); + if (ruleStyle) indent++; print(cases, ""); + if (ruleStyle) indent--; aPrint("}"); } + void printYieldExpression(JCTree tree) { + aPrint("yield "); + JCExpression value = readObject(tree, "value", null); + print(value); + println(";", tree); + } + @Override public void visitTry(JCTry tree) { aPrint("try "); List<?> resources = readObject(tree, "resources", List.nil()); @@ -1535,11 +1599,19 @@ public class PrettyPrinter extends JCTree.Visitor { // Starting with JDK9, this is inside the import list, but we've already printed it. Just ignore it. } else if ("JCSwitchExpression".equals(simpleName)) { // Introduced as preview feature in JDK12 printSwitchExpression(tree); + } else if ("JCYield".equals(simpleName)) { // Introduced as preview feature in JDK13, part of switch expressions. + printYieldExpression(tree); } else { throw new AssertionError("Unhandled tree type: " + tree.getClass() + ": " + tree); } } + private boolean isCaseRuleStyle(JCCase tree) { + if (tree == null) return false; + Enum<?> caseKind = readObject(tree, "caseKind", null); // JDK 12+ + return caseKind != null && caseKind.name().equalsIgnoreCase("RULE"); + } + private boolean jcAnnotatedTypeInit = false; private Class<?> jcAnnotatedTypeClass = null; diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index 5c31e87a..056852c8 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -24,7 +24,6 @@ package lombok.eclipse.agent; import lombok.permit.Permit; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.internal.compiler.CompilationResult; -import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -54,7 +53,6 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; -import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import java.lang.reflect.Field; @@ -455,7 +453,7 @@ public class PatchVal { } } - private static Field getField(Class clazz, String name) { + private static Field getField(Class<?> clazz, String name) { try { return Permit.getField(clazz, name); } catch (NoSuchFieldException e) { diff --git a/src/installer/lombok/installer/eclipse/EclipseProductLocation.java b/src/installer/lombok/installer/eclipse/EclipseProductLocation.java index aa97a3e5..73f98a35 100644 --- a/src/installer/lombok/installer/eclipse/EclipseProductLocation.java +++ b/src/installer/lombok/installer/eclipse/EclipseProductLocation.java @@ -294,10 +294,12 @@ public final class EclipseProductLocation extends IdeLocation { try { lombokJar.delete(); } catch (Throwable ignore) { /* Nothing we can do about that. */ } - if (!readSucceeded) throw new InstallException( - "I can't read my own jar file. I think you've found a bug in this installer!\nI suggest you restart it " + + if (!readSucceeded) { + throw new InstallException( + "I can't read my own jar file (trying: " + ourJar.toString() + "). I think you've found a bug in this installer!\nI suggest you restart it " + "and use the 'what do I do' link, to manually install lombok. Also, tell us about this at:\n" + - "http://groups.google.com/group/project-lombok - Thanks!", e); + "http://groups.google.com/group/project-lombok - Thanks!\n\n[DEBUG INFO] " + e.getClass() + ": " + e.getMessage() + "\nBase: " + OsUtils.class.getResource("OsUtils.class"), e); + } throw new InstallException("I can't write to your " + descriptor.getProductName() + " directory at " + name + generateWriteErrorMessage(), e); } } diff --git a/src/stubs/com/sun/tools/javac/parser/Tokens.java b/src/stubs/com/sun/tools/javac/parser/Tokens.java index 6e0aa479..f86bcefa 100644 --- a/src/stubs/com/sun/tools/javac/parser/Tokens.java +++ b/src/stubs/com/sun/tools/javac/parser/Tokens.java @@ -2,7 +2,7 @@ package com.sun.tools.javac.parser; public class Tokens { public static class Token { - + public int pos; } public interface Comment { diff --git a/src/utils/lombok/javac/CommentCatcher.java b/src/utils/lombok/javac/CommentCatcher.java index f8b73b0a..90266c26 100644 --- a/src/utils/lombok/javac/CommentCatcher.java +++ b/src/utils/lombok/javac/CommentCatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 The Project Lombok Authors. + * Copyright (C) 2011-2019 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 @@ -35,9 +35,10 @@ import com.sun.tools.javac.util.Context; public class CommentCatcher { private final JavaCompiler compiler; public static final FieldAugment<JCCompilationUnit, List<CommentInfo>> JCCompilationUnit_comments = FieldAugment.augment(JCCompilationUnit.class, List.class, "lombok$comments"); + public static final FieldAugment<JCCompilationUnit, List<Integer>> JCCompilationUnit_textBlockStarts = FieldAugment.augment(JCCompilationUnit.class, List.class, "lombok$textBlockStarts"); - public static CommentCatcher create(Context context) { - registerCommentsCollectingScannerFactory(context); + public static CommentCatcher create(Context context, boolean findTextBlocks) { + registerCommentsCollectingScannerFactory(context, findTextBlocks); JavaCompiler compiler = new JavaCompiler(context); setInCompiler(compiler, context); @@ -69,7 +70,12 @@ public class CommentCatcher { return list == null ? Collections.<CommentInfo>emptyList() : list; } - private static void registerCommentsCollectingScannerFactory(Context context) { + public List<Integer> getTextBlockStarts(JCCompilationUnit ast) { + List<Integer> list = JCCompilationUnit_textBlockStarts.get(ast); + return list == null ? Collections.<Integer>emptyList() : list; + } + + private static void registerCommentsCollectingScannerFactory(Context context, boolean findTextBlocks) { try { Class<?> scannerFactory; int javaCompilerVersion = Javac.getJavaCompilerVersion(); @@ -79,6 +85,7 @@ public class CommentCatcher { scannerFactory = Class.forName("lombok.javac.java7.CommentCollectingScannerFactory"); } else { scannerFactory = Class.forName("lombok.javac.java8.CommentCollectingScannerFactory"); + if (findTextBlocks) Permit.getField(scannerFactory, "findTextBlocks").set(null, true); } Permit.getMethod(scannerFactory, "preRegister", Context.class).invoke(null, context); } catch (InvocationTargetException e) { diff --git a/src/utils/lombok/javac/java8/CommentCollectingParser.java b/src/utils/lombok/javac/java8/CommentCollectingParser.java index b49312cb..c1dc2f7e 100644 --- a/src/utils/lombok/javac/java8/CommentCollectingParser.java +++ b/src/utils/lombok/javac/java8/CommentCollectingParser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 The Project Lombok Authors. + * Copyright (C) 2013-2019 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 @@ -22,10 +22,7 @@ package lombok.javac.java8; import static lombok.javac.CommentCatcher.JCCompilationUnit_comments; - -import java.util.List; - -import lombok.javac.CommentInfo; +import static lombok.javac.CommentCatcher.JCCompilationUnit_textBlockStarts; import com.sun.tools.javac.parser.JavacParser; import com.sun.tools.javac.parser.Lexer; @@ -44,8 +41,8 @@ class CommentCollectingParser extends JavacParser { public JCCompilationUnit parseCompilationUnit() { JCCompilationUnit result = super.parseCompilationUnit(); if (lexer instanceof CommentCollectingScanner) { - List<CommentInfo> comments = ((CommentCollectingScanner)lexer).getComments(); - JCCompilationUnit_comments.set(result, comments); + JCCompilationUnit_comments.set(result, ((CommentCollectingScanner) lexer).getComments()); + JCCompilationUnit_textBlockStarts.set(result, ((CommentCollectingScanner) lexer).getTextBlockStarts()); } return result; } diff --git a/src/utils/lombok/javac/java8/CommentCollectingScanner.java b/src/utils/lombok/javac/java8/CommentCollectingScanner.java index b59a9390..5a0647cc 100644 --- a/src/utils/lombok/javac/java8/CommentCollectingScanner.java +++ b/src/utils/lombok/javac/java8/CommentCollectingScanner.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Project Lombok Authors. + * Copyright (C) 2013-2019 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 @@ -39,4 +39,8 @@ public class CommentCollectingScanner extends Scanner { public List<CommentInfo> getComments() { return tokenizer.getComments(); } + + public List<Integer> getTextBlockStarts() { + return tokenizer.getTextBlockStarts(); + } }
\ No newline at end of file diff --git a/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java b/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java index fa79ff67..cb0d2e12 100644 --- a/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java +++ b/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 The Project Lombok Authors. + * Copyright (C) 2011-2019 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 @@ -28,7 +28,7 @@ import com.sun.tools.javac.parser.ScannerFactory; import com.sun.tools.javac.util.Context; public class CommentCollectingScannerFactory extends ScannerFactory { - + public static boolean findTextBlocks; @SuppressWarnings("all") public static void preRegister(final Context context) { if (context.get(scannerFactoryKey) == null) { @@ -76,7 +76,7 @@ public class CommentCollectingScannerFactory extends ScannerFactory { public Scanner newScanner(CharSequence input, boolean keepDocComments) { if (input instanceof CharBuffer) { CharBuffer buf = (CharBuffer) input; - return new CommentCollectingScanner(this, new CommentCollectingTokenizer(this, buf)); + return new CommentCollectingScanner(this, new CommentCollectingTokenizer(this, buf, findTextBlocks)); } char[] array = input.toString().toCharArray(); return newScanner(array, array.length, keepDocComments); @@ -84,6 +84,6 @@ public class CommentCollectingScannerFactory extends ScannerFactory { @Override public Scanner newScanner(char[] input, int inputLength, boolean keepDocComments) { - return new CommentCollectingScanner(this, new CommentCollectingTokenizer(this, input, inputLength)); + return new CommentCollectingScanner(this, new CommentCollectingTokenizer(this, input, inputLength, findTextBlocks)); } } diff --git a/src/utils/lombok/javac/java8/CommentCollectingTokenizer.java b/src/utils/lombok/javac/java8/CommentCollectingTokenizer.java index 1834fb00..08477e61 100644 --- a/src/utils/lombok/javac/java8/CommentCollectingTokenizer.java +++ b/src/utils/lombok/javac/java8/CommentCollectingTokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Project Lombok Authors. + * Copyright (C) 2013-2019 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 @@ -39,19 +39,30 @@ import com.sun.tools.javac.util.ListBuffer; class CommentCollectingTokenizer extends JavaTokenizer { private int prevEndPosition = 0; private final ListBuffer<CommentInfo> comments = new ListBuffer<CommentInfo>(); + private final ListBuffer<Integer> textBlockStarts; private int endComment = 0; - - CommentCollectingTokenizer(ScannerFactory fac, char[] buf, int inputLength) { + + CommentCollectingTokenizer(ScannerFactory fac, char[] buf, int inputLength, boolean findTextBlocks) { super(fac, new PositionUnicodeReader(fac, buf, inputLength)); + textBlockStarts = findTextBlocks ? new ListBuffer<Integer>() : null; } - CommentCollectingTokenizer(ScannerFactory fac, CharBuffer buf) { + CommentCollectingTokenizer(ScannerFactory fac, CharBuffer buf, boolean findTextBlocks) { super(fac, new PositionUnicodeReader(fac, buf)); + textBlockStarts = findTextBlocks ? new ListBuffer<Integer>() : null; + } + + int pos() { + return ((PositionUnicodeReader) reader).pos(); } @Override public Token readToken() { Token token = super.readToken(); - prevEndPosition = ((PositionUnicodeReader)reader).pos(); + prevEndPosition = pos(); + if (textBlockStarts != null && (prevEndPosition - token.pos > 5) && token.getClass().getSimpleName().equals("StringToken")) { + char[] start = reader.getRawCharacters(token.pos, token.pos + 3); + if (start[0] == '"' && start[1] == '"' && start[2] == '"') textBlockStarts.add(token.pos); + } return token; } @@ -113,6 +124,10 @@ class CommentCollectingTokenizer extends JavaTokenizer { return comments.toList(); } + public List<Integer> getTextBlockStarts() { + return textBlockStarts == null ? List.<Integer>nil() : textBlockStarts.toList(); + } + static class PositionUnicodeReader extends UnicodeReader { protected PositionUnicodeReader(ScannerFactory sf, char[] input, int inputLength) { super(sf, input, inputLength); diff --git a/src/utils/lombok/javac/java9/CommentCollectingParser.java b/src/utils/lombok/javac/java9/CommentCollectingParser.java index 307be405..034b6705 100644 --- a/src/utils/lombok/javac/java9/CommentCollectingParser.java +++ b/src/utils/lombok/javac/java9/CommentCollectingParser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 The Project Lombok Authors. + * Copyright (C) 2013-2019 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 @@ -22,10 +22,8 @@ package lombok.javac.java9; import static lombok.javac.CommentCatcher.JCCompilationUnit_comments; +import static lombok.javac.CommentCatcher.JCCompilationUnit_textBlockStarts; -import java.util.List; - -import lombok.javac.CommentInfo; import lombok.javac.java8.CommentCollectingScanner; import com.sun.tools.javac.parser.JavacParser; @@ -45,8 +43,8 @@ class CommentCollectingParser extends JavacParser { public JCCompilationUnit parseCompilationUnit() { JCCompilationUnit result = super.parseCompilationUnit(); if (lexer instanceof CommentCollectingScanner) { - List<CommentInfo> comments = ((CommentCollectingScanner)lexer).getComments(); - JCCompilationUnit_comments.set(result, comments); + JCCompilationUnit_comments.set(result, ((CommentCollectingScanner) lexer).getComments()); + JCCompilationUnit_textBlockStarts.set(result, ((CommentCollectingScanner) lexer).getTextBlockStarts()); } return result; } diff --git a/test/pretty/resource/after/Switch13.java b/test/pretty/resource/after/Switch13.java new file mode 100644 index 00000000..363b48dc --- /dev/null +++ b/test/pretty/resource/after/Switch13.java @@ -0,0 +1,55 @@ +public class Switch13 { + public void basic() { + switch (5) { + case 1: + case 2: + System.out.println("OK"); + break; + default: + } + } + + public void multiCase() { + switch (5) { + case 1, 2: + System.out.println("OK"); + default: + } + } + + public int switchExpr1() { + return switch (5) { + case 1, 2 -> 0; + case 3 -> { + yield 10; + } + default -> 10; + } + 10; + } + + public int switchExpr2() { + return switch (5) { + case 1, 2: + System.out.println("Hello"); + case 3: + yield 10; + default: + yield 20; + } + 10; + } + + public void arrowSwitch() { + switch (5) { + case 1, 2 -> System.out.println("Hello"); + case 3 -> { + System.out.println(""); + break; + } + } + } + + public void emptySwitch() { + switch (5) { + } + } +}
\ No newline at end of file diff --git a/test/pretty/resource/after/TextBlocks.java b/test/pretty/resource/after/TextBlocks.java new file mode 100644 index 00000000..4d2fc272 --- /dev/null +++ b/test/pretty/resource/after/TextBlocks.java @@ -0,0 +1,25 @@ +public class TextBlocks { + private String example = """ + This should not be indented. + line 2 + line 3 + """; + + private String ex2 = """ + This should be though. + """; + + private String ex3 = "This is a simple\nstring"; + + private String ex4 = """ + """; + + private String bizarroDent = """ + foo + bar""" + "hey" + """ + weird!"""; + + private String stringFolding = "hmmm" + """ + line1 + line2"""; +} diff --git a/test/pretty/resource/before/Switch12.java b/test/pretty/resource/before/Switch12.java index f1bd8a79..d708f93c 100644 --- a/test/pretty/resource/before/Switch12.java +++ b/test/pretty/resource/before/Switch12.java @@ -1,4 +1,4 @@ -// version 12: +// version 12:12 public class Switch12 { public void basic() { switch (5) { diff --git a/test/pretty/resource/before/Switch13.java b/test/pretty/resource/before/Switch13.java new file mode 100644 index 00000000..fe590788 --- /dev/null +++ b/test/pretty/resource/before/Switch13.java @@ -0,0 +1,56 @@ +// version 13: +public class Switch13 { + public void basic() { + switch (5) { + case 1: + case 2: + System.out.println("OK"); + break; + default: + } + } + + public void multiCase() { + switch (5) { + case 1, 2: + System.out.println("OK"); + default: + } + } + + public int switchExpr1() { + return switch (5) { + case 1, 2 -> 0; + case 3 -> { + yield 10; + } + default -> 10; + } + 10; + } + + public int switchExpr2() { + return switch (5) { + case 1, 2: + System.out.println("Hello"); + case 3: + yield 10; + default: + yield 20; + } + 10; + } + + public void arrowSwitch() { + switch (5) { + case 1, 2 -> System.out.println("Hello"); + case 3 -> { + System.out.println(""); + break; + } + } + } + + public void emptySwitch() { + switch (5) { + } + } +} diff --git a/test/pretty/resource/before/TextBlocks.java b/test/pretty/resource/before/TextBlocks.java new file mode 100644 index 00000000..247621c0 --- /dev/null +++ b/test/pretty/resource/before/TextBlocks.java @@ -0,0 +1,24 @@ +// version 13: +public class TextBlocks { + private String example = """ + This should not be indented.\nline 2 + line 3 + """; + + private String ex2 = """ + This should be though. + """; + + private String ex3 = "This is a simple\nstring"; + + private String ex4 = """ + """; + + private String bizarroDent = """ + foo\n bar""" + "hey" + """ + weird!"""; + + private String stringFolding = "hmmm" + """ + line1 + line2"""; +} diff --git a/test/transform/resource/after-delombok/WitherLegacyStar.java b/test/transform/resource/after-delombok/WitherLegacyStar.java new file mode 100644 index 00000000..69da9278 --- /dev/null +++ b/test/transform/resource/after-delombok/WitherLegacyStar.java @@ -0,0 +1,10 @@ +class WitherLegacyStar { + int i; + WitherLegacyStar(int i) { + this.i = i; + } + @java.lang.SuppressWarnings("all") + public WitherLegacyStar withI(final int i) { + return this.i == i ? this : new WitherLegacyStar(i); + } +} diff --git a/test/transform/resource/after-ecj/WitherLegacyStar.java b/test/transform/resource/after-ecj/WitherLegacyStar.java new file mode 100644 index 00000000..7aeaa969 --- /dev/null +++ b/test/transform/resource/after-ecj/WitherLegacyStar.java @@ -0,0 +1,11 @@ +import lombok.experimental.*; +class WitherLegacyStar { + @Wither int i; + WitherLegacyStar(int i) { + super(); + this.i = i; + } + public @java.lang.SuppressWarnings("all") WitherLegacyStar withI(final int i) { + return ((this.i == i) ? this : new WitherLegacyStar(i)); + } +}
\ No newline at end of file diff --git a/test/transform/resource/before/WitherLegacyStar.java b/test/transform/resource/before/WitherLegacyStar.java new file mode 100644 index 00000000..e1a58ae1 --- /dev/null +++ b/test/transform/resource/before/WitherLegacyStar.java @@ -0,0 +1,8 @@ +import lombok.experimental.*; +class WitherLegacyStar { + @Wither int i; + + WitherLegacyStar(int i) { + this.i = i; + } +} diff --git a/website/templates/features/With.html b/website/templates/features/With.html index 35c52dab..425a1640 100644 --- a/website/templates/features/With.html +++ b/website/templates/features/With.html @@ -14,13 +14,14 @@ </p><p> For example, if you create <code>public class Point { private final int x, y; }</code>, setters make no sense because the fields are final. <code>@With</code> can generate a <code>withX(int newXValue)</code> method for you which will return a new point with the supplied value for <code>x</code> and the same value for <code>y</code>. </p><p> + The <code>@With</code> relies on a constructor for all fields in order to do its work. If this constructor does not exist, your <code>@With</code> annotation will result in a compile time error message. You can use Lombok's own <a href="/features/constructor"><code>@AllArgsConstructor</code></a>, or as <a href="/features/Value"><code>Value</code></a> will automatically produce an all args constructor as well, you can use that too. It's of course also acceptable if you manually write this constructor. It must contain all non-static fields, in the same lexical order. + </p><p> Like <a href="/features/GetterSetter"><code>@Setter</code></a>, you can specify an access level in case you want the generated with method to be something other than <code>public</code>:<br /> <code>@With(level = AccessLevel.PROTECTED)</code>. Also like <a href="/features/GetterSetter"><code>@Setter</code></a>, you can also put a <code>@With</code> annotation on a type, which means a <code>with</code> method is generated for each field (even non-final fields). </p><p> To put annotations on the generated method, you can use <code>onMethod=@__({@AnnotationsHere})</code>. Be careful though! This is an experimental feature. For more details see the documentation on the <a href="/features/experimental/onX">onX</a> feature. </p><p> javadoc on the field will be copied to generated with methods. Normally, all text is copied, and <code>@param</code> is <em>moved</em> to the with method, whilst <code>@return</code> lines are stripped from the with method's javadoc. Moved means: Deleted from the field's javadoc. It is also possible to define unique text for the with method's javadoc. To do that, you create a 'section' named <code>WITH</code>. A section is a line in your javadoc containing 2 or more dashes, then the text 'WITH', followed by 2 or more dashes, and nothing else on the line. If you use sections, <code>@return</code> and <code>@param</code> stripping / copying for that section is no longer done (move the <code>@param</code> line into the section). - </p><p> - If you have a hierarchical immutable data structure, the <a href="/features/experimental/WithBy"><code>@WithBy</code></a> feature might be more suitable than <code>@With</code> + </p> </@f.overview> <@f.snippets name="With" /> @@ -45,8 +46,6 @@ </p><p> No method is generated if any method already exists with the same name (case insensitive) and same parameter count. For example, <code>withX(int x)</code> will not be generated if there's already a method <code>withX(String... x)</code> even though it is technically possible to make the method. This caveat exists to prevent confusion. If the generation of a method is skipped for this reason, a warning is emitted instead. Varargs count as 0 to N parameters. </p><p> - For <code>boolean</code> fields that start with <code>is</code> immediately followed by a title-case letter, nothing is prefixed to generate the wither name. - </p><p> Various well known annotations about nullity cause null checks to be inserted and will be copied to the parameter. See <a href="/features/GetterSetter">Getter/Setter</a> documentation's small print for more information. </p> </@f.smallPrint> diff --git a/website/templates/features/experimental/index.html b/website/templates/features/experimental/index.html index 112d30c4..960f4b1a 100644 --- a/website/templates/features/experimental/index.html +++ b/website/templates/features/experimental/index.html @@ -48,10 +48,6 @@ Don't lose your composition. </@main.feature> - <@main.feature title="@Wither" href="Wither"> - Immutable 'setters' - methods that create a clone but with one changed field. - </@main.feature> - <@main.feature title="onMethod= / onConstructor= / onParam=" href="onX"> Sup dawg, we heard you like annotations, so we put annotations in your annotations so you can annotate while you're annotating. </@main.feature> @@ -90,6 +86,9 @@ <@main.feature title="@Builder: promoted" href="/features/Builder"> <code>@Builder</code> is a solid base to build APIs on, and has been moved to the main package. </@main.feature> + <@main.feature title="@Wither: renamed to @With, and promoted" href="/features/With"> + Immutable 'setters' - methods that create a clone but with one changed field. + </@main.feature> </div> </div> </div> diff --git a/website/templates/features/index.html b/website/templates/features/index.html index 06b47c0b..9e2f53e8 100644 --- a/website/templates/features/index.html +++ b/website/templates/features/index.html @@ -66,6 +66,10 @@ <code>synchronized</code> done right: Don't expose your locks. </@main.feature> + <@main.feature title="@With" href="With"> + Immutable 'setters' - methods that create a clone but with one changed field. + </@main.feature> + <@main.feature title="@Getter(lazy=true)" href="GetterLazy"> Laziness is a virtue! </@main.feature> diff --git a/website/templates/order-license-info.html b/website/templates/order-license-info.html index f5839c0a..46dafd66 100644 --- a/website/templates/order-license-info.html +++ b/website/templates/order-license-info.html @@ -9,7 +9,7 @@ <p> Tidelift offers professional support of open source tools. When you have a Tidelift subscription, they will help you inventory all the various open source tools and libraries you use, give one central location to check compliance, and a single channel for release notes and security advisories. The bulk of the tidelift subscription fee is redistributed to major open source projects, and Project Lombok is one of those 'lifted' projects. Therefore, your Tidelift subscription helps maintain Project Lombok! </p><p> - We recommend a <a class="tidelift-link-inline" href="https://tidelift.com/subscription/pkg/maven-org-projectlombok-lombok?utm_source=lombok&utm_medium=referral&utm_campaign=orderdonate">Tidelift subscription</a> to any corporation that uses Project Lombok. + We recommend a <a class="tidelift-link-inline" href="https://tidelift.com/subscription/pkg/maven-org-projectlombok-lombok?utm_source=maven-org-projectlombok-lombok&utm_medium=referral&utm_campaign=orderdonate">Tidelift subscription</a> to any corporation that uses Project Lombok. </div> <div class="row text-center"> <h2>Order a professional or enterprise license</h2> diff --git a/website/templates/setup/javac.html b/website/templates/setup/javac.html index 48b0ebcf..96bd8c2b 100644 --- a/website/templates/setup/javac.html +++ b/website/templates/setup/javac.html @@ -11,15 +11,13 @@ <p> Support for JDK9, if you haven't modularized your own projects yet (no <code>module-info.java</code> yet), is included in lombok starting with version 1.16.20. Just use lombok as normal: <code> javac -cp lombok.jar ...</code> </p><p> - Support for JDK9 if you did modularize your own projects (you've written a <code>module-info.java</code> file) is available in the <a href="/download-edge">edge release</a>. To use it: <code>javac -cp lombok.jar -p lombok.jar ...</code><br /> + Support for JDK9 if you did modularize your own projects (you've written a <code>module-info.java</code> file):<br /><code>javac -cp lombok.jar -p lombok.jar ...</code><br /> Note that you will have to add lombok to your <code>module-info.java</code> file:<pre> module <em>myapp</em> { requires static lombok; }</pre> </p><p> The 'static' part ensures that you won't need lombok to be present at runtime. - </p><p> - Feedback about JDK9 module-info support can be given at <a href="https://github.com/rzwitserloot/lombok/issues/985">github issue #985</a>. </p> </@s.section> </@s.scaffold> diff --git a/website/templates/tidelift.html b/website/templates/tidelift.html index 7f0ab88b..3d020d7a 100644 --- a/website/templates/tidelift.html +++ b/website/templates/tidelift.html @@ -14,7 +14,7 @@ </p> </div> <div class="row text-center"> - <a class="tideliftb tideliftb1" href="https://tidelift.com/subscription/pkg/maven-org-projectlombok-lombok?utm_source=lombok&utm_medium=referral&utm_campaign=enterprise">LEARN MORE</a> <a class="tideliftb tideliftb2" href="https://tidelift.com/subscription/request-a-demo?utm_source=lombok&utm_medium=referral&utm_campaign=enterprise">REQUEST A DEMO</a> + <a class="tideliftb tideliftb1" href="https://tidelift.com/subscription/pkg/maven-org-projectlombok-lombok?utm_source=maven-org-projectlombok-lombok&utm_medium=referral&utm_campaign=enterprise">LEARN MORE</a> <a class="tideliftb tideliftb2" href="https://tidelift.com/subscription/request-a-demo?utm_source=maven-org-projectlombok-lombok&utm_medium=referral&utm_campaign=enterprise">REQUEST A DEMO</a> </div> <div class="row text-center"> <h2>The Tidelift Subscription manages your dependencies for you</h2> diff --git a/website/usageExamples/ToStringExample_post.jpage b/website/usageExamples/ToStringExample_post.jpage index 67e78f20..9e41b26b 100644 --- a/website/usageExamples/ToStringExample_post.jpage +++ b/website/usageExamples/ToStringExample_post.jpage @@ -8,7 +8,7 @@ public class ToStringExample { private int id; public String getName() { - return this.getName(); + return this.name; } public static class Square extends Shape { diff --git a/website/usageExamples/WithExample_post.jpage b/website/usageExamples/WithExample_post.jpage index a881ed8d..c19e26e4 100644 --- a/website/usageExamples/WithExample_post.jpage +++ b/website/usageExamples/WithExample_post.jpage @@ -1,8 +1,8 @@ import lombok.NonNull; public class WithExample { - private final int age; private @NonNull final String name; + private final int age; public WithExample(String name, int age) { if (name == null) throw new NullPointerException(); @@ -10,12 +10,12 @@ public class WithExample { this.age = age; } - public WithExample withAge(int age) { - return this.age == age ? this : new WithExample(name, age); - } - protected WithExample withName(@NonNull String name) { if (name == null) throw new java.lang.NullPointerException("name"); return this.name == name ? this : new WithExample(name, age); } + + public WithExample withAge(int age) { + return this.age == age ? this : new WithExample(name, age); + } } diff --git a/website/usageExamples/WithExample_pre.jpage b/website/usageExamples/WithExample_pre.jpage index 3c76204e..2f78020a 100644 --- a/website/usageExamples/WithExample_pre.jpage +++ b/website/usageExamples/WithExample_pre.jpage @@ -3,8 +3,8 @@ import lombok.NonNull; import lombok.With; public class WithExample { - @With private final int age; @With(AccessLevel.PROTECTED) @NonNull private final String name; + @With private final int age; public WithExample(String name, int age) { if (name == null) throw new NullPointerException(); |