diff options
74 files changed, 1678 insertions, 225 deletions
@@ -35,6 +35,7 @@ Robbert Jan Grootjans <grootjans@gmail.com> Robert Wertman <robert.wertman@gmail.com> Roel Spilker <r.spilker@gmail.com> Roland Praml <pram@gmx.de> +Rostislav Krasny <45571812+rosti-il@users.noreply.github.com> Samuel Pereira <samuel.p.araujo@gmail.com> Sander Koning <askoning@gmail.com> Szymon Pacanowski <spacanowski@gmail.com> diff --git a/buildScripts/create-eclipse-project.ant.xml b/buildScripts/create-eclipse-project.ant.xml index 513ffe8f..da91964f 100644 --- a/buildScripts/create-eclipse-project.ant.xml +++ b/buildScripts/create-eclipse-project.ant.xml @@ -27,7 +27,7 @@ This buildfile is part of projectlombok.org. It creates the infrastructure neede <target name="eclipse" depends="eclipse.projectfiles, eclipse.testtarget.default" description="Downloads dependencies, create eclipse project files, as well as debug/run test targets. Open this directory as project in eclipse (via import... existing projects)" /> <target name="eclipse.projectfiles" depends="deps"> - <ivy:eclipsegen source="1.6"> + <ivy:eclipsegen source="1.6" srcout="bin/main"> <srcdir dir="src/core" /> <srcdir dir="src/core8" /> <srcdir dir="src/launch" /> @@ -35,7 +35,7 @@ This buildfile is part of projectlombok.org. It creates the infrastructure neede <srcdir dir="src/eclipseAgent" /> <srcdir dir="src/installer" /> <srcdir dir="src/delombok" /> - <srcdir dir="src/stubs" /> + <srcdir dir="src/stubs" srcout="bin/stubs" /> <srcdir dir="src/support" /> <srcdir dir="experimental/src" /> <srcdir dir="test/transform/src" /> @@ -116,7 +116,7 @@ This buildfile is part of projectlombok.org. It creates the infrastructure neede <condition property="inputs.jvmtarget" value="1.6"><equals arg1="${inputs.jvmtarget.raw}" arg2="6" /></condition> <condition property="inputs.jvmtarget" value="1.8"><equals arg1="${inputs.jvmtarget.raw}" arg2="8" /></condition> - <property name="inputs" value="${inputs.jvmtarget.raw}" /> + <property name="inputs.jvmtarget" value="${inputs.jvmtarget.raw}" /> <condition property="inputs.bootpath" value="${jdk6-rt.loc}"><equals arg1="${inputs.jvmtarget.raw}" arg2="6" /></condition> <condition property="inputs.bootpath" value="${jdk8-rt.loc}"><equals arg1="${inputs.jvmtarget.raw}" arg2="8" /></condition> @@ -127,6 +127,12 @@ This buildfile is part of projectlombok.org. It creates the infrastructure neede <target name="eclipse.testtarget.javac" depends="compile.support" description="Makes an eclipse launch target for running the tests for javac"> <eclipse.testtarget.conf.jvmtarget question="Which javac do you want to target? Enter a version, such as '11'." validargs="8,11,13,14,15" /> + <local name="inputs.confcp" /> + <condition property="inputs.confcp" value="NONE"> + <not><equals arg1="8" arg2="${inputs.jvmtarget.raw}" /></not> + </condition> + <property name="inputs.confcp" refid="cp.javac8" /> + <java classname="lombok.eclipseCreate.CreateEclipseDebugTarget" failonerror="true"> <classpath> <path refid="cp.buildtools" /> diff --git a/buildScripts/info.ant.xml b/buildScripts/info.ant.xml index 55fea8af..fe6e0ee3 100644 --- a/buildScripts/info.ant.xml +++ b/buildScripts/info.ant.xml @@ -258,6 +258,11 @@ build/website. 'pack' bzips this up, ready to ship to the server. 'publish' sends this to the server and runs a script to deploy. + > ant website.open + + First builds the website, then hosts it locally and opens it in your browser so + you can see the website in its full, template-applied form. + > ant latest-changes.build Makes a changelog variant that lists only the newest changes; it is included diff --git a/buildScripts/ivy.xml b/buildScripts/ivy.xml index 14530f06..3dfbde7e 100644 --- a/buildScripts/ivy.xml +++ b/buildScripts/ivy.xml @@ -58,6 +58,7 @@ <dependency org="projectlombok.org" name="markdownj" rev="1.02b4" conf="buildtools->build" /> <dependency org="de.java2html" name="java2html" rev="5.0" conf="buildtools->default" /> <dependency org="org.freemarker" name="freemarker" rev="2.3.28" conf="buildtools->default" /> + <dependency org="com.sparkjava" name="spark-core" rev="2.9.2" conf="buildtools->default" /> <dependency org="org.eclipse.jgit" name="org.eclipse.jgit.ant" rev="5.2.0.201812061821-r" conf="supporters->default" /> <dependency org="org.eclipse.jgit" name="org.eclipse.jgit" rev="5.2.0.201812061821-r" conf="supporters->default" /> @@ -93,6 +94,7 @@ <dependency org="org.eclipse.jdt" name="org.eclipse.jdt.core" rev="3.22.0" conf="eclipse-202006->default" transitive="false" /> <dependency org="org.eclipse.jdt" name="org.eclipse.jdt.ui" rev="3.21.100" conf="eclipse-202006->default" transitive="false" /> <dependency org="org.eclipse.platform" name="org.eclipse.equinox.common" rev="3.12.0" conf="eclipse-202006->default" transitive="false" /> + <dependency org="org.eclipse.platform" name="org.eclipse.equinox.preferences" rev="3.8.0" conf="eclipse-202006->default" transitive="false" /> <dependency org="org.eclipse.platform" name="org.eclipse.core.resources" rev="3.13.700" conf="eclipse-202006->default" transitive="false" /> <dependency org="org.eclipse.platform" name="org.eclipse.core.jobs" rev="3.10.800" conf="eclipse-202006->default" transitive="false" /> <dependency org="org.eclipse.platform" name="org.eclipse.osgi" rev="3.15.300" conf="eclipse-202006->default" transitive="false" /> diff --git a/buildScripts/setup.ant.xml b/buildScripts/setup.ant.xml index 9b96240d..ed835960 100644 --- a/buildScripts/setup.ant.xml +++ b/buildScripts/setup.ant.xml @@ -106,7 +106,7 @@ This buildfile is part of projectlombok.org. It sets up the build itself. <target name="-ipp.load" depends="-ipp.download"> <taskdef classpath="lib/ivyplusplus.jar" resource="com/zwitserloot/ivyplusplus/antlib.xml" uri="antlib:com.zwitserloot.ivyplusplus" /> - <ivy:ensureippversion version="1.36" property="ivyplusplus.minimumAvailable" /> + <ivy:ensureippversion version="1.38" property="ivyplusplus.minimumAvailable" /> </target> <target name="-ipp.redownload" unless="ivyplusplus.minimumAvailable"> diff --git a/buildScripts/tests.ant.xml b/buildScripts/tests.ant.xml index f78bfae9..cb840048 100644 --- a/buildScripts/tests.ant.xml +++ b/buildScripts/tests.ant.xml @@ -119,6 +119,7 @@ This buildfile is part of projectlombok.org. It takes care of compiling and runn <classpath refid="cp.stripe" /> <classpath location="build/lombok" /> <classpath location="build/tests" /> + <classpath location="build/teststubs" /> <test name="lombok.TestJavac" /> </junit> </sequential> diff --git a/buildScripts/vm-finder.ant.xml b/buildScripts/vm-finder.ant.xml index 2b2c4c4c..a15d8ed0 100644 --- a/buildScripts/vm-finder.ant.xml +++ b/buildScripts/vm-finder.ant.xml @@ -38,7 +38,7 @@ This buildfile is part of projectlombok.org. It contains platform specific code <condition property="jvm.loc" value="${jvm.loc.force}"> <and> <isset property="jvm.loc.force" /> - <not><matches string="${jvm.loc.force}" pattern="^\$\{jvm[0-9\.]+\.loc\}$" /></not> + <not><matches string="${jvm.loc.force}" pattern="^\$\{jvm\.locations\.j[0-9\.]+\}$" /></not> </and> </condition> <condition property="jvm.loc.invalid"> diff --git a/buildScripts/website.ant.xml b/buildScripts/website.ant.xml index 9efb2668..730b79c2 100644 --- a/buildScripts/website.ant.xml +++ b/buildScripts/website.ant.xml @@ -150,6 +150,19 @@ such as applying the templates to produce the website, converting the changelog <website.make version="${lombok.version.live}" fullversion="${lombok.fullversion.live}" cmd="all" /> </target> + <target name="website.open" depends="website.build, compile.support" description="Builds the website, then serves it locally, opening a browser."> + <local name="dir.build.website" /> + <property name="dir.build.website" location="build/website" /> + <java classname="lombok.website.RunSite" failonerror="true"> + <classpath> + <path refid="cp.buildtools" /> + <pathelement location="build/support" /> + </classpath> + <arg value="${dir.build.website}" /> + <arg value="open" /> + </java> + </target> + <target name="website.print-all-versions" depends="compile.support"> <java classname="lombok.website.WebsiteMaker" failonerror="true"> <classpath> diff --git a/doc/changelog.markdown b/doc/changelog.markdown index b7fbdd7d..91fdd6a5 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -2,7 +2,6 @@ Lombok Changelog ---------------- ### v1.18.13 "Edgy Guinea Pig" -* POTENTIAL FIX: Kotlin incremental compiler on gradle 3.6.1 [Issue #2412](https://github.com/rzwitserloot/lombok/issues/2412) * PERFORMANCE: Several performance improvements during parsing/compilation, both using javac and Eclipse. Thanks __@Rawi01__! * PERFORMANCE: The generated equals method will first compare primitives, then primitive wrappers and then reference fields. Manual re-ordering is possible using `@Include(rank=n)`. [Pull Request #2485](https://github.com/rzwitserloot/lombok/pull/2485), [Issue #1543](https://github.com/rzwitserloot/lombok/issues/1543) * IMPROBABLE BREAKING CHANGE: The generated hashcode has changed for classes that include both primitive fields and reference fields. diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 60b98a94..01a4b576 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -140,7 +140,7 @@ public class ConfigurationKeys { * NB: GWT projects, and probably android projects, should explicitly set this key to {@code true} for the entire project. * * <br> - * <em>BREAKING CHANGE</em>: Starting with lombok v1.16.20, defaults to {@code false} instead of {@code true}, as {@code @ConstructorProperties} requires extra modules in JDK9. + * <em>BREAKING CHANGE</em>: Starting with lombok v1.16.20, defaults to {@code true} instead of {@code false}, as {@code @ConstructorProperties} requires extra modules in JDK9. * * @see ConfigurationKeys#ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES * @deprecated Since version 2.0, use {@link #ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES} instead. diff --git a/src/core/lombok/core/PostCompiler.java b/src/core/lombok/core/PostCompiler.java index e17f806e..72f4b3a2 100644 --- a/src/core/lombok/core/PostCompiler.java +++ b/src/core/lombok/core/PostCompiler.java @@ -72,10 +72,12 @@ public final class PostCompiler { // no need to call super byte[] original = toByteArray(); byte[] copy = null; - try { - copy = applyTransformations(original, fileName, diagnostics); - } catch (Exception e) { - diagnostics.addWarning(String.format("Error during the transformation of '%s'; no post-compilation has been applied", fileName)); + if (original.length > 0) { + try { + copy = applyTransformations(original, fileName, diagnostics); + } catch (Exception e) { + diagnostics.addWarning(String.format("Error during the transformation of '%s'; no post-compilation has been applied", fileName)); + } } if (copy == null) { diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 1c4437d7..92a9a142 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.AllArgsConstructor; @@ -756,4 +757,67 @@ public class HandlerUtil { if (PRIMITIVE_WRAPPER_TYPE_NAME_PATTERN.matcher(typeName).matches()) return 800; return 0; } + + private static final Pattern SECTION_FINDER = Pattern.compile("^\\s*\\**\\s*[-*][-*]+\\s*([GS]ETTER|WITH(?:ER)?)\\s*[-*][-*]+\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + private static final Pattern LINE_BREAK_FINDER = Pattern.compile("(\\r?\\n)?"); + + public static String stripLinesWithTagFromJavadoc(String javadoc, String regexpFragment) { + Pattern p = Pattern.compile("^\\s*\\**\\s*" + regexpFragment + "\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(javadoc); + return m.replaceAll(""); + } + + public static String stripSectionsFromJavadoc(String javadoc) { + Matcher sectionMatcher = SECTION_FINDER.matcher(javadoc); + if (!sectionMatcher.find()) return javadoc; + + return javadoc.substring(0, sectionMatcher.start()); + } + + public static String getJavadocSection(String javadoc, String sectionNameSpec) { + String[] sectionNames = sectionNameSpec.split("\\|"); + Matcher sectionMatcher = SECTION_FINDER.matcher(javadoc); + Matcher lineBreakMatcher = LINE_BREAK_FINDER.matcher(javadoc); + int sectionStart = -1; + int sectionEnd = -1; + while (sectionMatcher.find()) { + boolean found = false; + for (String sectionName : sectionNames) if (sectionMatcher.group(1).equalsIgnoreCase(sectionName)) { + found = true; + break; + } + if (found) { + lineBreakMatcher.find(sectionMatcher.end()); + sectionStart = lineBreakMatcher.end(); + } else if (sectionStart != -1) { + sectionEnd = sectionMatcher.start(); + } + } + + if (sectionStart != -1) { + if (sectionEnd != -1) return javadoc.substring(sectionStart, sectionEnd); + return javadoc.substring(sectionStart); + } + + return null; + } + + private static final Pattern FIND_RETURN = Pattern.compile("^\\s*\\**\\s*@returns?\\s+.*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + + public static String addReturnsThisIfNeeded(String in) { + if (FIND_RETURN.matcher(in).find()) return in; + + return addJavadocLine(in, "@return {@code this}."); + } + + public static String addReturnsUpdatedSelfIfNeeded(String in) { + if (FIND_RETURN.matcher(in).find()) return in; + + return addJavadocLine(in, "@return a clone of this object, except with this updated property (returns {@code this} if an identical value is passed)."); + } + + public static String addJavadocLine(String in, String line) { + if (in.endsWith("\n")) return in + line + "\n"; + return in + "\n" + line; + } } diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java index b45ac72d..d53856af 100644 --- a/src/core/lombok/eclipse/EclipseAST.java +++ b/src/core/lombok/eclipse/EclipseAST.java @@ -350,7 +350,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { if (!changed) clearChanged(); } - private static boolean isComplete(CompilationUnitDeclaration unit) { + public static boolean isComplete(CompilationUnitDeclaration unit) { return (unit.bits & ASTNode.HasAllMethodBodies) != 0; } diff --git a/src/core/lombok/eclipse/EclipseAugments.java b/src/core/lombok/eclipse/EclipseAugments.java index f4583ac4..783e25c9 100644 --- a/src/core/lombok/eclipse/EclipseAugments.java +++ b/src/core/lombok/eclipse/EclipseAugments.java @@ -21,9 +21,15 @@ */ package lombok.eclipse; +import java.util.Map; +import java.util.List; +import java.util.concurrent.ConcurrentMap; + import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.core.CompilationUnit; +import org.eclipse.jdt.internal.core.SourceMethod; import lombok.core.FieldAugment; @@ -36,4 +42,6 @@ public final class EclipseAugments { public static final FieldAugment<ASTNode, Boolean> ASTNode_handled = FieldAugment.augment(ASTNode.class, boolean.class, "lombok$handled"); public static final FieldAugment<ASTNode, ASTNode> ASTNode_generatedBy = FieldAugment.augment(ASTNode.class, ASTNode.class, "$generatedBy"); public static final FieldAugment<Annotation, Boolean> Annotation_applied = FieldAugment.augment(Annotation.class, boolean.class, "lombok$applied"); + public static final FieldAugment<CompilationUnit, Map<String, String>> CompilationUnit_javadoc = FieldAugment.augment(CompilationUnit.class, Map.class, "$javadoc"); + public static final FieldAugment<CompilationUnit, ConcurrentMap<String, List<SourceMethod>>> CompilationUnit_delegateMethods = FieldAugment.augment(CompilationUnit.class, ConcurrentMap.class, "$delegateMethods"); } diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java index 6fcde937..59a0709e 100644 --- a/src/core/lombok/eclipse/TransformEclipseAST.java +++ b/src/core/lombok/eclipse/TransformEclipseAST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2019 The Project Lombok Authors. + * Copyright (C) 2009-2020 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 @@ -24,6 +24,9 @@ package lombok.eclipse; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Field; +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; import lombok.ConfigurationKeys; import lombok.core.LombokConfiguration; @@ -63,6 +66,7 @@ public class TransformEclipseAST { public static boolean disableLombok = false; private static final HistogramTracker lombokTracker; + private static Map<CompilationUnitDeclaration, State> transformationStates = Collections.synchronizedMap(new WeakHashMap<CompilationUnitDeclaration, State>()); static { String v = System.getProperty("lombok.histogram"); @@ -130,6 +134,30 @@ public class TransformEclipseAST { } /** + * Check if lombok already handled the given AST. This method will return + * <code>true</code> once for diet mode and once for full mode. + * + * The reason for this is that Eclipse invokes the transform method multiple + * times during compilation and it is enough to transform it once and not + * repeat the whole thing over and over again. + * + * @param ast The AST node belonging to the compilation unit (java speak for a single source file). + * @return <code>true</code> if this AST was already handled by lombok. + */ + public static boolean alreadyTransformed(CompilationUnitDeclaration ast) { + State state = transformationStates.get(ast); + + if (state == State.FULL) return true; + if (state == State.DIET) { + if (!EclipseAST.isComplete(ast)) return true; + transformationStates.put(ast, State.FULL); + } else { + transformationStates.put(ast, State.DIET); + } + return false; + } + + /** * This method is called immediately after Eclipse finishes building a CompilationUnitDeclaration, which is * the top-level AST node when Eclipse parses a source file. The signature is 'magic' - you should not * change it! @@ -144,6 +172,7 @@ public class TransformEclipseAST { if (disableLombok) return; if (Symbols.hasSymbol("lombok.disable")) return; + if (alreadyTransformed(ast)) return; // Do NOT abort if (ast.bits & ASTNode.HasAllMethodBodies) != 0 - that doesn't work. @@ -243,4 +272,9 @@ public class TransformEclipseAST { nextPriority = Math.min(nextPriority, handlers.handleAnnotation(top, annotationNode, annotation, priority)); } } + + private static enum State { + DIET, + FULL + } } diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 6bfcf16e..d85c2ee8 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -39,6 +39,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; @@ -94,6 +96,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; @@ -103,6 +106,7 @@ 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.lookup.WildcardBinding; +import org.eclipse.jdt.internal.core.CompilationUnit; import lombok.AccessLevel; import lombok.ConfigurationKeys; @@ -2470,18 +2474,7 @@ public class EclipseHandlerUtil { } public static NameReference createNameReference(String name, Annotation source) { - int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long)pS << 32 | pE; - - char[][] nameTokens = fromQualifiedName(name); - long[] pos = new long[nameTokens.length]; - Arrays.fill(pos, p); - - QualifiedNameReference nameReference = new QualifiedNameReference(nameTokens, pos, pS, pE); - nameReference.statementEnd = pE; - - setGeneratedBy(nameReference, source); - return nameReference; + return generateQualifiedNameRef(source, fromQualifiedName(name)); } private static long[] copy(long[] array) { @@ -2635,4 +2628,154 @@ public class EclipseHandlerUtil { setGeneratedBy(ref, source); return ref; } + + public static TypeReference createTypeReference(String typeName, ASTNode source) { + return generateQualifiedTypeRef(source, fromQualifiedName(typeName)); + } + + public static String getDocComment(CompilationUnitDeclaration cud, ASTNode node) { + ICompilationUnit compilationUnit = cud.compilationResult.compilationUnit; + if (node instanceof FieldDeclaration) { + FieldDeclaration fieldDeclaration = (FieldDeclaration) node; + char[] rawContent = CharOperation.subarray(compilationUnit.getContents(), fieldDeclaration.declarationSourceStart, fieldDeclaration.declarationSourceEnd); + String rawContentString = new String(rawContent); + int startIndex = rawContentString.indexOf("/**"); + int endIndex = rawContentString.indexOf("*/"); + if (startIndex != -1 && endIndex != -1) { + /* Remove all leading asterisks */ + return rawContentString.substring(startIndex + 3, endIndex).replaceAll("(?m)^\\s*\\* ?", "").trim(); + } + } + return null; + } + + public static void setDocComment(CompilationUnitDeclaration cud, EclipseNode eclipseNode, String doc) { + setDocComment(cud, (TypeDeclaration) upToTypeNode(eclipseNode).get(), eclipseNode.get(), doc); + } + + public static void setDocComment(CompilationUnitDeclaration cud, TypeDeclaration type, ASTNode node, String doc) { + if (cud.compilationResult.compilationUnit instanceof CompilationUnit) { + CompilationUnit compilationUnit = (CompilationUnit) cud.compilationResult.compilationUnit; + Map<String, String> docs = CompilationUnit_javadoc.setIfAbsent(compilationUnit, new HashMap<String, String>()); + + if (node instanceof AbstractMethodDeclaration) { + AbstractMethodDeclaration methodDeclaration = (AbstractMethodDeclaration) node; + String signature = getSignature(type, methodDeclaration); + /* Add javadoc start marker, add leading asterisks to each line, add javadoc end marker */ + docs.put(signature, String.format("/**%n%s%n */", doc.replaceAll("(?m)^", " * "))); + } + } + } + + public static String getSignature(TypeDeclaration type, AbstractMethodDeclaration methodDeclaration) { + StringBuilder sb = new StringBuilder(); + sb.append(type.name); + sb.append("."); + sb.append(methodDeclaration.selector); + sb.append("("); + Argument[] arguments = methodDeclaration.arguments; + if (arguments != null) { + for (Argument argument : arguments) { + String signature = Signature.createTypeSignature(argument.type.getLastToken(), false); + sb.append(signature); + } + } + sb.append(")"); + return sb.toString(); + } + + public static enum CopyJavadoc { + VERBATIM { + @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { + return getDocComment(cu, node.get()); + } + }, + GETTER { + @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { + final ASTNode n = node.get(); + String javadoc = getDocComment(cu, n); + // step 1: Check if there is a 'GETTER' section. If yes, that becomes the new method's javadoc. + String out = getJavadocSection(javadoc, "GETTER"); + final boolean sectionBased = out != null; + if (!sectionBased) { + out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@param(?:eter)?\\s+.*"); + } + return out; + } + }, + SETTER { + @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { + return applySetter(cu, node, "SETTER"); + } + }, + WITH { + @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { + return addReturnsUpdatedSelfIfNeeded(applySetter(cu, node, "WITH|WITHER")); + } + }, + WITH_BY { + @Override public String apply(final CompilationUnitDeclaration cu, final EclipseNode node) { + return applySetter(cu, node, "WITHBY|WITH_BY"); + } + }; + + public abstract String apply(final CompilationUnitDeclaration cu, final EclipseNode node); + + private static String applySetter(final CompilationUnitDeclaration cu, EclipseNode node, String sectionName) { + final ASTNode n = node.get(); + String javadoc = getDocComment(cu, n); + // step 1: Check if there is a 'SETTER' section. If yes, that becomes the new method's javadoc. + String out = getJavadocSection(javadoc, sectionName); + final boolean sectionBased = out != null; + if (!sectionBased) { + out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@returns?\\s+.*"); + } + return shouldReturnThis(node) ? addReturnsThisIfNeeded(out) : out; + } + } + + /** + * Copies javadoc on one node to the other. + * + * This one is a shortcut for {@link EclipseHandlerUtil#copyJavadoc(EclipseNode, ASTNode, TypeDeclaration, CopyJavadoc, boolean)} + * if source and target node are in the same type. + */ + public static void copyJavadoc(EclipseNode from, ASTNode to, CopyJavadoc copyMode) { + copyJavadoc(from, to, (TypeDeclaration) upToTypeNode(from).get(), copyMode, false); + } + + /** + * Copies javadoc on one node to the other. + * + * This one is a shortcut for {@link EclipseHandlerUtil#copyJavadoc(EclipseNode, ASTNode, TypeDeclaration, CopyJavadoc, boolean)} + * if source and target node are in the same type. + */ + public static void copyJavadoc(EclipseNode from, ASTNode to, CopyJavadoc copyMode, boolean forceAddReturn) { + copyJavadoc(from, to, (TypeDeclaration) upToTypeNode(from).get(), copyMode, forceAddReturn); + } + + public static void copyJavadoc(EclipseNode from, ASTNode to, TypeDeclaration type, CopyJavadoc copyMode) { + copyJavadoc(from, to, type, copyMode, false); + } + + /** + * Copies javadoc on one node to the other. + * + * in 'GETTER' copyMode, first a 'GETTER' segment is searched for. If it exists, that will become the javadoc for the 'to' node, and this section is + * stripped out of the 'from' node. If no 'GETTER' segment is found, then the entire javadoc is taken minus any {@code @param} lines and other sections. + * any {@code @return} lines are stripped from 'from'. + * + * in 'SETTER' mode, stripping works similarly to 'GETTER' mode, except {@code param} are copied and stripped from the original and {@code @return} are skipped. + */ + public static void copyJavadoc(EclipseNode from, ASTNode to, TypeDeclaration type, CopyJavadoc copyMode, boolean forceAddReturn) { + if (copyMode == null) copyMode = CopyJavadoc.VERBATIM; + try { + CompilationUnitDeclaration cud = ((CompilationUnitDeclaration) from.top().get()); + String newJavadoc = copyMode.apply(cud, from); + if (newJavadoc != null) { + if (forceAddReturn) newJavadoc = addReturnsThisIfNeeded(newJavadoc); + setDocComment(cud, type, to, newJavadoc); + } + } catch (Exception ignore) {} + } } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 801fe7e7..9e6c28e8 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -21,8 +21,8 @@ */ package lombok.eclipse.handlers; -import static lombok.eclipse.Eclipse.*; import static lombok.core.handlers.HandlerUtil.*; +import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Modifier; @@ -30,6 +30,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -84,14 +86,16 @@ import lombok.ConfigurationKeys; import lombok.Singular; import lombok.ToString; import lombok.core.AST.Kind; -import lombok.core.handlers.HandlerUtil; -import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.core.configuration.CheckerFrameworkVersion; +import lombok.core.handlers.HandlerUtil; +import lombok.core.handlers.HandlerUtil.FieldAccess; +import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.EclipseHandlerUtil.CopyJavadoc; import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; @@ -965,9 +969,29 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { typeReference.annotations[0] = new Annotation[] {ann}; setter.receiver = new Receiver(new char[] { 't', 'h', 'i', 's' }, 0, typeReference, null, Modifier.FINAL); } + if (sourceNode.up().getKind() == Kind.METHOD) { + copyJavadocFromParam(originalFieldNode.up(), setter, td, paramName.toString()); + } else { + copyJavadoc(originalFieldNode, setter, td, CopyJavadoc.SETTER, true); + } injectMethod(builderType, setter); } + private void copyJavadocFromParam(EclipseNode from, MethodDeclaration to, TypeDeclaration type, String param) { + try { + CompilationUnitDeclaration cud = (CompilationUnitDeclaration) from.top().get(); + String methodComment = getDocComment(cud, from.get()); + if (methodComment == null) return; + + Pattern pattern = Pattern.compile("@param " + param + " (\\S|\\s)+?(?=^ ?@)", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(methodComment); + if (matcher.find()) { + String newJavadoc = addReturnsThisIfNeeded(matcher.group()); + setDocComment(cud, type, to, newJavadoc); + } + } catch (Exception ignore) {} + } + public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source, AccessLevel access) { TypeDeclaration parent = (TypeDeclaration) tdParent.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java index 9343011f..cee3912c 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java @@ -171,10 +171,10 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldName injectMethod(fieldsType, constructor); } + Clinit cli = null; if (genClinit) { - Clinit cli = new Clinit(parent.compilationResult); + cli = new Clinit(parent.compilationResult); injectMethod(fieldsType, cli); - cli.traverse(generatedByVisitor, ((TypeDeclaration) fieldsType.get()).scope); } for (EclipseNode fieldNode : fields) { @@ -202,5 +202,9 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldName injectField(fieldsType, constantField); constantField.traverse(generatedByVisitor, ((TypeDeclaration) fieldsType.get()).initializerScope); } + + if (genClinit) { + cli.traverse(generatedByVisitor, ((TypeDeclaration) fieldsType.get()).scope); + } } } diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java index 9cd1e2a1..4d7d84ce 100644 --- a/src/core/lombok/eclipse/handlers/HandleGetter.java +++ b/src/core/lombok/eclipse/handlers/HandleGetter.java @@ -296,6 +296,7 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> { } method.traverse(new SetGeneratedByVisitor(source), parent.scope); + copyJavadoc(fieldNode, method, CopyJavadoc.GETTER); return method; } diff --git a/src/core/lombok/eclipse/handlers/HandleLog.java b/src/core/lombok/eclipse/handlers/HandleLog.java index a0e431e5..8297426d 100644 --- a/src/core/lombok/eclipse/handlers/HandleLog.java +++ b/src/core/lombok/eclipse/handlers/HandleLog.java @@ -22,11 +22,9 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.handleFlagUsage; -import static lombok.eclipse.Eclipse.fromQualifiedName; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Modifier; -import java.util.Arrays; import java.util.List; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -35,7 +33,6 @@ import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; -import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; @@ -156,19 +153,6 @@ public class HandleLog { return fieldDecl; } - public static TypeReference createTypeReference(String typeName, Annotation source) { - int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long) pS << 32 | pE; - - char[][] typeNameTokens = fromQualifiedName(typeName); - long[] pos = new long[typeNameTokens.length]; - Arrays.fill(pos, p); - - TypeReference typeReference = new QualifiedTypeReference(typeNameTokens, pos); - setGeneratedBy(typeReference, source); - return typeReference; - } - private static final Expression[] createFactoryParameters(ClassLiteralAccess loggingType, Annotation source, List<LogFactoryParameter> parameters, Expression loggerTopic) { Expression[] expressions = new Expression[parameters.size()]; int pS = source.sourceStart, pE = source.sourceEnd; diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index cb2ca3bf..83c6eef2 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -263,6 +263,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { if (returnType != null && returnStatement != null) createRelevantNonNullAnnotation(sourceNode, method); method.traverse(new SetGeneratedByVisitor(source), parent.scope); + copyJavadoc(fieldNode, method, CopyJavadoc.SETTER, returnStatement != null); return method; } } diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index b1e7c419..79b7e816 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -741,6 +741,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } out.statements = body.isEmpty() ? null : body.toArray(new Statement[0]); + out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return out; } diff --git a/src/core/lombok/eclipse/handlers/HandleWith.java b/src/core/lombok/eclipse/handlers/HandleWith.java index 83357710..dce193d8 100644 --- a/src/core/lombok/eclipse/handlers/HandleWith.java +++ b/src/core/lombok/eclipse/handlers/HandleWith.java @@ -290,6 +290,7 @@ public class HandleWith extends EclipseAnnotationHandler<With> { EclipseHandlerUtil.createRelevantNonNullAnnotation(fieldNode, method); method.traverse(new SetGeneratedByVisitor(source), parent.scope); + copyJavadoc(fieldNode, method, CopyJavadoc.WITH); return method; } } diff --git a/src/core/lombok/eclipse/handlers/HandleWithBy.java b/src/core/lombok/eclipse/handlers/HandleWithBy.java index 5f229aaf..f56004f6 100644 --- a/src/core/lombok/eclipse/handlers/HandleWithBy.java +++ b/src/core/lombok/eclipse/handlers/HandleWithBy.java @@ -373,6 +373,7 @@ public class HandleWithBy extends EclipseAnnotationHandler<WithBy> { createRelevantNonNullAnnotation(fieldNode, method); method.traverse(new SetGeneratedByVisitor(source), parent.scope); + copyJavadoc(fieldNode, method, CopyJavadoc.WITH_BY); return method; } } diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index e0af0e52..2379d0a0 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -35,11 +35,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.lang.model.element.Element; +import com.sun.tools.javac.code.Attribute; import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Scope; @@ -1127,7 +1127,25 @@ public class JavacHandlerUtil { } } } - + + static class JCAnnotationReflect { + private static Field ATTRIBUTE; + + static { + try { + ATTRIBUTE = Permit.getField(JCAnnotation.class, "attribute"); + } catch (Exception ignore) {} + } + + static Attribute.Compound getAttribute(JCAnnotation jcAnnotation) { + try { + return (Attribute.Compound) ATTRIBUTE.get(jcAnnotation); + } catch (Exception e) { + return null; + } + } + } + // jdk9 support, types have changed, names stay the same static class ClassSymbolMembersField { private static final Field membersField; @@ -1178,8 +1196,10 @@ public class JavacHandlerUtil { * Also takes care of updating the JavacAST. */ public static void injectMethod(JavacNode typeNode, JCMethodDecl method, List<Type> paramTypes, Type returnType) { + Context context = typeNode.getContext(); + Symtab symtab = Symtab.instance(context); JCClassDecl type = (JCClassDecl) typeNode.get(); - + if (method.getName().contentEquals("<init>")) { //Scan for default constructor, and remove it. int idx = 0; @@ -1196,7 +1216,7 @@ public class JavacHandlerUtil { idx++; } } - + addSuppressWarningsAll(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext()); addGenerated(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext()); type.defs = type.defs.append(method); @@ -1204,18 +1224,38 @@ public class JavacHandlerUtil { List<Symbol.VarSymbol> params = null; if (method.getParameters() != null && !method.getParameters().isEmpty()) { ListBuffer<Symbol.VarSymbol> newParams = new ListBuffer<Symbol.VarSymbol>(); - for (JCTree.JCVariableDecl param : method.getParameters()) { - if (param.sym != null) newParams.append(param.sym); + for (int i = 0; i < method.getParameters().size(); i++) { + JCTree.JCVariableDecl param = method.getParameters().get(i); + if (param.sym == null) { + Type paramType = paramTypes == null ? param.getType().type : paramTypes.get(i); + VarSymbol varSymbol = new VarSymbol(param.mods.flags, param.name, paramType, symtab.noSymbol); + List<JCAnnotation> annotations = param.getModifiers().getAnnotations(); + if (annotations != null && !annotations.isEmpty()) { + ListBuffer<Attribute.Compound> newAnnotations = new ListBuffer<Attribute.Compound>(); + for (JCAnnotation jcAnnotation : annotations) { + Attribute.Compound attribute = JCAnnotationReflect.getAttribute(jcAnnotation); + if (attribute != null) { + newAnnotations.append(attribute); + } + } + if (annotations.length() == newAnnotations.length()) { + varSymbol.appendAttributes(newAnnotations.toList()); + } + } + newParams.append(varSymbol); + } else { + newParams.append(param.sym); + } } params = newParams.toList(); if (params.length() != method.getParameters().length()) params = null; } fixMethodMirror(typeNode.getContext(), typeNode.getElement(), method.getModifiers().flags, method.getName(), paramTypes, params, returnType); - + typeNode.add(method, Kind.METHOD); } - + private static void fixMethodMirror(Context context, Element typeMirror, long access, Name methodName, List<Type> paramTypes, List<Symbol.VarSymbol> params, Type returnType) { if (typeMirror == null || paramTypes == null || returnType == null) return; ClassSymbol cs = (ClassSymbol) typeMirror; @@ -1963,47 +2003,6 @@ public class JavacHandlerUtil { return (JCExpression) in; } - private static final Pattern SECTION_FINDER = Pattern.compile("^\\s*\\**\\s*[-*][-*]+\\s*([GS]ETTER|WITH(?:ER)?)\\s*[-*][-*]+\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); - - public static String stripLinesWithTagFromJavadoc(String javadoc, String regexpFragment) { - Pattern p = Pattern.compile("^\\s*\\**\\s*" + regexpFragment + "\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); - Matcher m = p.matcher(javadoc); - return m.replaceAll(""); - } - - public static String stripSectionsFromJavadoc(String javadoc) { - Matcher m = SECTION_FINDER.matcher(javadoc); - if (!m.find()) return javadoc; - - return javadoc.substring(0, m.start()); - } - - public static String getJavadocSection(String javadoc, String sectionNameSpec) { - String[] sectionNames = sectionNameSpec.split("\\|"); - Matcher m = SECTION_FINDER.matcher(javadoc); - int sectionStart = -1; - int sectionEnd = -1; - while (m.find()) { - boolean found = false; - for (String sectionName : sectionNames) if (m.group(1).equalsIgnoreCase(sectionName)) { - found = true; - break; - } - if (found) { - sectionStart = m.end() + 1; - } else if (sectionStart != -1) { - sectionEnd = m.start(); - } - } - - if (sectionStart != -1) { - if (sectionEnd != -1) return javadoc.substring(sectionStart, sectionEnd); - return javadoc.substring(sectionStart); - } - - return null; - } - public static enum CopyJavadoc { VERBATIM { @Override public String apply(final JCCompilationUnit cu, final JavacNode node) { @@ -2100,24 +2099,6 @@ public class JavacHandlerUtil { } catch (Exception ignore) {} } - private static final Pattern FIND_RETURN = Pattern.compile("^\\s*\\**\\s*@returns?\\s+.*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); - static String addReturnsThisIfNeeded(String in) { - if (FIND_RETURN.matcher(in).find()) return in; - - return addJavadocLine(in, "@return {@code this}."); - } - - static String addReturnsUpdatedSelfIfNeeded(String in) { - if (FIND_RETURN.matcher(in).find()) return in; - - return addJavadocLine(in, "@return a clone of this object, except with this updated property (returns {@code this} if an identical value is passed)."); - } - - static String addJavadocLine(String in, String line) { - if (in.endsWith("\n")) return in + line + "\n"; - return in + "\n" + line; - } - public static boolean isDirectDescendantOfObject(JavacNode typeNode) { if (!(typeNode.get() instanceof JCClassDecl)) throw new IllegalArgumentException("not a type node"); JCTree extending = Javac.getExtendsClause((JCClassDecl) typeNode.get()); diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java index 208b215f..54fa4ebf 100644 --- a/src/delombok/lombok/delombok/PrettyPrinter.java +++ b/src/delombok/lombok/delombok/PrettyPrinter.java @@ -188,12 +188,6 @@ public class PrettyPrinter extends JCTree.Visitor { return getEndPosition(tree, compilationUnit); } - private static int lineEndPos(String s, int start) { - int pos = s.indexOf('\n', start); - if (pos < 0) pos = s.length(); - return pos; - } - private boolean needsAlign, needsNewLine, onNewLine = true, needsSpace, aligned; public static final class UncheckedIOException extends RuntimeException { @@ -434,23 +428,25 @@ public class PrettyPrinter extends JCTree.Visitor { private void printDocComment(JCTree tree) { String dc = getJavadocFor(tree); if (dc == null) return; + aPrintln("/**"); - int pos = 0; - int endpos = lineEndPos(dc, pos); boolean atStart = true; - while (pos < dc.length()) { - String line = dc.substring(pos, endpos); - if (line.trim().isEmpty() && atStart) { + + for (String line : dc.split("\\r?\\n")) { + if (atStart && line.trim().isEmpty()) { atStart = false; continue; } + atStart = false; aPrint(" *"); - if (pos < dc.length() && dc.charAt(pos) > ' ') print(" "); - println(dc.substring(pos, endpos)); - pos = endpos + 1; - endpos = lineEndPos(dc, pos); + if (!line.isEmpty() && !Character.isWhitespace(line.charAt(0))) { + print(" "); + } + + println(line); } + aPrintln(" */"); } diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java index d57ab549..3412000e 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java @@ -104,6 +104,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { patchRetrieveEllipsisStartPosition(sm); patchRetrieveRightBraceOrSemiColonPosition(sm); patchRetrieveProperRightBracketPosition(sm); + patchRetrieveStartBlockPosition(sm); patchSetGeneratedFlag(sm); patchDomAstReparseIssues(sm); patchHideGeneratedNodes(sm); @@ -126,6 +127,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { patchExtensionMethod(sm, ecjOnly); patchRenameField(sm); patchNullCheck(sm); + patchJavadoc(sm); if (reloadExistingClasses) sm.reloadClasses(instrumentation); } @@ -414,6 +416,13 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build()); } + private static void patchRetrieveStartBlockPosition(ScriptManager sm) { + sm.addScript(ScriptBuilder.wrapReturnValue() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveStartBlockPosition")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveStartBlockPosition", "int", "int", "int")) + .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build()); + } + private static void patchRetrieveRightBraceOrSemiColonPosition(ScriptManager sm) { sm.addScript(ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration")) @@ -450,7 +459,15 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { sm.addScript(ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "extractSubArrayType", "org.eclipse.jdt.core.dom.ArrayType", "org.eclipse.jdt.core.dom.ArrayType", "int", "int")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveProperRightBracketPosition", "int", "int", "int")) - .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveProperRightBracketPosition", "int", "int", "org.eclipse.jdt.core.dom.ArrayType")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveProperRightBracketPosition", "int", "int", "org.eclipse.jdt.core.dom.Type")) + .requestExtra(StackRequest.PARAM1) + .transplant() + .build()); + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToArray", "org.eclipse.jdt.core.dom.ArrayType", "org.eclipse.jdt.core.dom.Type", "int", "int", "int", "org.eclipse.jdt.internal.compiler.ast.Annotation[][]")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveProperRightBracketPosition", "int", "int", "int")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveProperRightBracketPosition", "int", "int", "org.eclipse.jdt.core.dom.Type")) .requestExtra(StackRequest.PARAM1) .transplant() .build()); @@ -507,6 +524,8 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationFragment", "org.eclipse.jdt.core.dom.VariableDeclarationFragment", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration")) .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationFragment", "org.eclipse.jdt.core.dom.VariableDeclarationFragment", "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")) .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationStatement", "org.eclipse.jdt.core.dom.VariableDeclarationStatement", "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")) + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "createBaseType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference", "long[]", "org.eclipse.jdt.internal.compiler.ast.Annotation[][]", "char[][]", "int", "int", "boolean")) + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "createQualifiedType", "org.eclipse.jdt.core.dom.QualifiedType", "org.eclipse.jdt.internal.compiler.ast.TypeReference", "long[]", "org.eclipse.jdt.internal.compiler.ast.Annotation[][]", "char[][]", "int", "org.eclipse.jdt.core.dom.Type")) /* Targets above are only patched because the resulting dom nodes should be marked if generated. */ .request(StackRequest.PARAM1, StackRequest.RETURN_VALUE) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", @@ -542,6 +561,43 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void", "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); + + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.PrimitiveType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ParameterizedType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); + + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); /* Set generated flag for QualifiedNames */ sm.addScript(ScriptBuilder.wrapMethodCall() @@ -655,6 +711,26 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .request(StackRequest.THIS) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Delegate", "handleDelegateForType", "boolean", "java.lang.Object")) .build()); + + sm.addScript(ScriptBuilder.setSymbolDuringMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.core.SelectionRequestor", "acceptSourceMethod")) + .callToWrap(new Hook("org.eclipse.jdt.core.IType", "getMethods", "org.eclipse.jdt.core.IMethod[]")) + .symbol("lombok.skipdelegates") + .build()); + + sm.addScript(ScriptBuilder.addField() + .fieldName("$delegateMethods") + .fieldType("Ljava/util/Map;") + .setPublic() + .setTransient() + .targetClass("org.eclipse.jdt.internal.core.CompilationUnit") + .build()); + + sm.addScript(ScriptBuilder.wrapReturnValue() + .target(new MethodTarget("org.eclipse.jdt.internal.core.SourceTypeElementInfo", "getChildren", "org.eclipse.jdt.core.IJavaElement[]")) + .request(StackRequest.RETURN_VALUE, StackRequest.THIS) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Delegate", "getChildren", "java.lang.Object[]", "java.lang.Object", "java.lang.Object")) + .build()); } private static void addPatchesForValEclipse(ScriptManager sm) { @@ -838,5 +914,37 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .request(StackRequest.PARAM1) .transplant().build()); } + + private static void patchJavadoc(ScriptManager sm) { + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContent", "java.lang.String", "org.eclipse.jdt.core.IJavaElement", "boolean")) + .methodToWrap(new Hook("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContentFromSource", "java.lang.String", "org.eclipse.jdt.core.IJavaElement")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Javadoc", "getHTMLContentFromSource", "java.lang.String", "java.lang.String", "org.eclipse.jdt.core.IJavaElement")) + .requestExtra(StackRequest.PARAM1) + .build()); + + /* This is an older version that uses IMember instead of IJavaElement */ + sm.addScript(ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContent", "java.lang.String", "org.eclipse.jdt.core.IMember", "boolean")) + .methodToWrap(new Hook("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContentFromSource", "java.lang.String", "org.eclipse.jdt.core.IMember")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Javadoc", "getHTMLContentFromSource", "java.lang.String", "java.lang.String", "org.eclipse.jdt.core.IJavaElement")) + .requestExtra(StackRequest.PARAM1) + .build()); + + sm.addScript(ScriptBuilder.replaceMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.compiler.ast.TypeDeclaration", "printBody", "java.lang.StringBuffer", "int", "java.lang.StringBuffer")) + .methodToReplace(new Hook("org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration", "print", "java.lang.StringBuffer", "int", "java.lang.StringBuffer")) + .replacementMethod(new Hook("lombok.launch.PatchFixesHider$Javadoc", "printMethod", "java.lang.StringBuffer", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration", "int", "java.lang.StringBuffer", "org.eclipse.jdt.internal.compiler.ast.TypeDeclaration")) + .requestExtra(StackRequest.THIS) + .build()); + + sm.addScript(ScriptBuilder.addField() + .fieldName("$javadoc") + .fieldType("Ljava/util/Map;") + .setPublic() + .setTransient() + .targetClass("org.eclipse.jdt.internal.core.CompilationUnit") + .build()); + } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java index 46ce63f9..833a8226 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java @@ -22,10 +22,15 @@ package lombok.eclipse.agent; import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccStatic; +import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccVarargs; import java.util.Arrays; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.CompletionProposal; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.codeassist.CompletionEngine; import org.eclipse.jdt.internal.codeassist.InternalCompletionProposal; @@ -35,16 +40,29 @@ import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameRefere import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.core.NameLookup; public class ExtensionMethodCompletionProposal extends InternalCompletionProposal { + private char[] fullSignature; + private char[][] parameterNames; + public ExtensionMethodCompletionProposal(final int replacementOffset) { super(CompletionProposal.METHOD_REF, replacementOffset - 1); } public void setMethodBinding(final MethodBinding method, final ASTNode node) { + // Add proposal parameter names, sometimes its empty... + if (method.parameterNames != null && method.parameterNames.length > 0) { + setParameterNames(Arrays.copyOfRange(method.parameterNames, 1, method.parameterNames.length)); + } else { + // Copy signature for parameter name resolution, this is more reliable but slower + fullSignature = CompletionEngine.getSignature(method); + } + MethodBinding original = method.original(); TypeBinding[] parameters = Arrays.copyOf(method.parameters, method.parameters.length); method.parameters = Arrays.copyOfRange(method.parameters, 1, method.parameters.length); + TypeBinding[] originalParameters = null; if (original != method) { originalParameters = Arrays.copyOf(method.original().parameters, method.original().parameters.length); @@ -76,6 +94,10 @@ public class ExtensionMethodCompletionProposal extends InternalCompletionProposa setName(method.selector); setCompletion(completion); setFlags(method.modifiers & (~AccStatic)); + // Remove varargs flag if it is the only parameter + if (method.isVarargs() && length == 0) { + setFlags(getFlags() & (~AccVarargs)); + } int index = node.sourceEnd + 1; if (node instanceof CompletionOnQualifiedNameReference) { index -= ((CompletionOnQualifiedNameReference) node).completionIdentifier.length; @@ -96,4 +118,58 @@ public class ExtensionMethodCompletionProposal extends InternalCompletionProposa method.original().parameters = originalParameters; } } + + @Override + public char[][] findParameterNames(IProgressMonitor monitor) { + if (parameterNames != null) { + return parameterNames; + } + + NameLookup.Answer answer = this.nameLookup.findType( + new String(this.declarationTypeName), + new String(this.declarationPackageName), + false, + NameLookup.ACCEPT_CLASSES & NameLookup.ACCEPT_INTERFACES, + true/* consider secondary types */, + false/* do NOT wait for indexes */, + false/*don't check restrictions*/, + null); + + if (answer != null && answer.type != null) { + char[][] parameterTypes = Signature.getParameterTypes(fullSignature); + + String[] args = new String[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + args[i] = new String(parameterTypes[i]); + } + IMethod method = answer.type.getMethod(new String(this.getName()), args); + IMethod[] methods = answer.type.findMethods(method); + if (methods != null && methods.length > 0) { + method = methods[0]; + } + if (method != null) { + try { + String[] parameterNames = method.getParameterNames(); + char[][] parameterNamesAsChar = new char[parameterNames.length - 1][]; + for (int i = 0; i < parameterNamesAsChar.length; i++) { + parameterNamesAsChar[i] = parameterNames[i + 1].toCharArray(); + } + setParameterNames(parameterNamesAsChar); + } catch (JavaModelException e) { + // Nope + } + } + } + // Seems like we failed, fallback + if (parameterNames == null) { + parameterNames = super.findParameterNames(monitor); + } + return parameterNames; + } + + @Override + public void setParameterNames(char[][] parameterNames) { + this.parameterNames = parameterNames; + super.setParameterNames(parameterNames); + } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java index 1a287d93..e92ed674 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Project Lombok Authors. + * Copyright (C) 2010-2020 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,8 +22,8 @@ package lombok.eclipse.agent; import static lombok.eclipse.Eclipse.*; +import static lombok.eclipse.EclipseAugments.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; -import static lombok.eclipse.EclipseAugments.Annotation_applied; import java.lang.reflect.Method; import java.util.ArrayList; @@ -34,14 +34,16 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; -import lombok.core.AST.Kind; -import lombok.eclipse.Eclipse; -import lombok.eclipse.EclipseAST; -import lombok.eclipse.EclipseNode; -import lombok.eclipse.TransformEclipseAST; -import lombok.eclipse.handlers.SetGeneratedByVisitor; - +import org.eclipse.jdt.core.ElementChangedEvent; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.ILocalVariable; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -81,8 +83,27 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.UnresolvedReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; +import org.eclipse.jdt.internal.core.CompilationUnit; +import org.eclipse.jdt.internal.core.DeltaProcessor; +import org.eclipse.jdt.internal.core.JavaElement; +import org.eclipse.jdt.internal.core.JavaElementDelta; +import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.LocalVariable; +import org.eclipse.jdt.internal.core.SourceMethod; +import org.eclipse.jdt.internal.core.SourceMethodInfo; +import org.eclipse.jdt.internal.core.SourceType; +import org.eclipse.jdt.internal.core.SourceTypeElementInfo; + +import lombok.core.AST.Kind; +import lombok.eclipse.Eclipse; +import lombok.eclipse.EclipseAST; +import lombok.eclipse.EclipseNode; +import lombok.eclipse.TransformEclipseAST; +import lombok.eclipse.handlers.SetGeneratedByVisitor; +import lombok.patcher.Symbols; public class PatchDelegate { + private static class ClassScopeEntry { ClassScopeEntry(ClassScope scope) { this.scope = scope; @@ -123,6 +144,12 @@ public class PatchDelegate { public static boolean handleDelegateForType(ClassScope scope) { if (TransformEclipseAST.disableLombok) return false; + + CompilationUnitDeclaration cud = scope.compilationUnitScope().referenceContext; + if (scope == scope.compilationUnitScope().topLevelTypes[0].scope) { + cleanupDelegateMethods(cud); + } + if (!hasDelegateMarkedFieldsOrMethods(scope.referenceContext)) return false; List<ClassScopeEntry> stack = visited.get(); @@ -149,7 +176,6 @@ public class PatchDelegate { try { TypeDeclaration decl = scope.referenceContext; if (decl != null) { - CompilationUnitDeclaration cud = scope.compilationUnitScope().referenceContext; EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true); List<BindingTuple> methodsToDelegate = new ArrayList<BindingTuple>(); fillMethodBindingsForFields(cud, scope, methodsToDelegate); @@ -168,12 +194,25 @@ public class PatchDelegate { } } finally { stack.remove(stack.size() - 1); + if (stack.isEmpty()) { + notifyDelegateMethodsAdded(cud); + } } } return false; } + public static IJavaElement[] getChildren(IJavaElement[] returnValue, SourceTypeElementInfo javaElement) { + if (Symbols.hasSymbol("lombok.skipdelegates")) return returnValue; + + List<SourceMethod> delegateMethods = getDelegateMethods((SourceType) javaElement.getHandle()); + if (delegateMethods != null) { + return concat(returnValue, delegateMethods.toArray(new IJavaElement[0]), IJavaElement.class); + } + return returnValue; + } + /** * Returns a string containing the signature of a method that appears (erased) at least twice in the list. * If no duplicates are present, {@code null} is returned. @@ -419,6 +458,11 @@ public class PatchDelegate { private static void generateDelegateMethods(EclipseNode typeNode, List<BindingTuple> methods, DelegateReceiver delegateReceiver) { CompilationUnitDeclaration top = (CompilationUnitDeclaration) typeNode.top().get(); + + String qualifiedName = new String(CharOperation.concatWith(getQualifiedInnerName(typeNode.up(), typeNode.getName().toCharArray()), '$')); + SourceType sourceType = getSourceType(top, qualifiedName); + List<SourceMethod> delegateSourceMethods = getDelegateMethods(sourceType); + for (BindingTuple pair : methods) { EclipseNode annNode = typeNode.getAst().get(pair.responsible); MethodDeclaration method = createDelegateMethod(pair.fieldName, typeNode, pair, top.compilationResult, annNode, delegateReceiver); @@ -426,6 +470,10 @@ public class PatchDelegate { SetGeneratedByVisitor visitor = new SetGeneratedByVisitor(annNode.get()); method.traverse(visitor, ((TypeDeclaration)typeNode.get()).scope); injectMethod(typeNode, method); + + if (delegateSourceMethods != null) { + delegateSourceMethods.add(DelegateSourceMethod.forMethodDeclaration(sourceType, method)); + } } } } @@ -673,6 +721,129 @@ public class PatchDelegate { return method; } + private static void cleanupDelegateMethods(CompilationUnitDeclaration cud) { + CompilationUnit compilationUnit = getCompilationUnit(cud); + if (compilationUnit != null) { + CompilationUnit_delegateMethods.clear(compilationUnit); + } + } + + private static boolean javaModelManagerAvailable = true; + private static void notifyDelegateMethodsAdded(CompilationUnitDeclaration cud) { + CompilationUnit compilationUnit = getCompilationUnit(cud); + if (compilationUnit != null && javaModelManagerAvailable) { + try { + DeltaProcessor deltaProcessor = JavaModelManager.getJavaModelManager().getDeltaProcessor(); + deltaProcessor.fire(new JavaElementDelta(compilationUnit), ElementChangedEvent.POST_CHANGE); + } catch (NoClassDefFoundError e) { + javaModelManagerAvailable = false; + } + } + } + + private static CompilationUnit getCompilationUnit(Object iCompilationUnit) { + if (iCompilationUnit instanceof CompilationUnit) { + CompilationUnit compilationUnit = (CompilationUnit) iCompilationUnit; + return compilationUnit.originalFromClone(); + } + return null; + } + + private static CompilationUnit getCompilationUnit(CompilationUnitDeclaration cud) { + return getCompilationUnit(cud.compilationResult.compilationUnit); + } + + private static final class DelegateSourceMethod extends SourceMethod { + private DelegateSourceMethodInfo sourceMethodInfo; + + private static DelegateSourceMethod forMethodDeclaration(JavaElement parent, MethodDeclaration method) { + Argument[] arguments = method.arguments != null ? method.arguments : new Argument[0]; + String[] parameterTypes = new String[arguments.length]; + for (int i = 0; i < arguments.length; i++) { + parameterTypes[i] = Signature.createTypeSignature(CharOperation.concatWith(arguments[i].type.getParameterizedTypeName(), '.'), false); + } + return new DelegateSourceMethod(parent, new String(method.selector), parameterTypes, method); + } + + private DelegateSourceMethod(JavaElement parent, String name, String[] parameterTypes, MethodDeclaration md) { + super(parent, name, parameterTypes); + sourceMethodInfo = new DelegateSourceMethodInfo(this, md); + } + + @Override public Object getElementInfo() throws JavaModelException { + return sourceMethodInfo; + } + + /** + * Disable refactoring for delegate methods + */ + @Override public boolean isReadOnly() { + return true; + } + + /** + * This is required to prevent duplicate entries in the outline + */ + @Override public boolean equals(Object o) { + return this == o; + } + + public static final class DelegateSourceMethodInfo extends SourceMethodInfo { + DelegateSourceMethodInfo(DelegateSourceMethod delegateSourceMethod, MethodDeclaration md) { + int pS = md.sourceStart; + int pE = md.sourceEnd; + + Argument[] methodArguments = md.arguments != null ? md.arguments : new Argument[0]; + char[][] argumentNames = new char[methodArguments.length][]; + arguments = new ILocalVariable[methodArguments.length]; + for (int i = 0; i < methodArguments.length; i++) { + Argument argument = methodArguments[i]; + argumentNames[i] = argument.name; + arguments[i] = new LocalVariable(delegateSourceMethod, new String(argument.name), pS, pE, pS, pS, delegateSourceMethod.getParameterTypes()[i], argument.annotations, argument.modifiers, true); + } + setArgumentNames(argumentNames); + + setSourceRangeStart(pS); + setSourceRangeEnd(pE); + setNameSourceStart(pS); + setNameSourceEnd(pE); + + setExceptionTypeNames(CharOperation.NO_CHAR_CHAR); + setReturnType(md.returnType == null ? new char[]{'v', 'o','i', 'd'} : CharOperation.concatWith(md.returnType.getParameterizedTypeName(), '.')); + setFlags(md.modifiers); + } + } + } + + private static List<SourceMethod> getDelegateMethods(SourceType sourceType) { + if (sourceType != null) { + CompilationUnit compilationUnit = getCompilationUnit(sourceType.getCompilationUnit()); + if (compilationUnit != null) { + ConcurrentMap<String, List<SourceMethod>> map = CompilationUnit_delegateMethods.setIfAbsent(compilationUnit, new ConcurrentHashMap<String, List<SourceMethod>>()); + List<SourceMethod> newList = new ArrayList<SourceMethod>(); + List<SourceMethod> oldList = map.putIfAbsent(sourceType.getTypeQualifiedName(), newList); + return oldList != null ? oldList : newList; + } + } + return null; + } + + private static SourceType getSourceType(CompilationUnitDeclaration cud, String typeName) { + CompilationUnit compilationUnit = getCompilationUnit(cud); + if (compilationUnit != null) { + try { + for (IType type : compilationUnit.getAllTypes()) { + if (type instanceof SourceType && type.getTypeQualifiedName().equals(typeName)) { + return (SourceType) type; + } + } + } catch (JavaModelException e) { + // Ignore + } + } + return null; + } + private static final class Reflection { public static final Method classScopeBuildFieldsAndMethodsMethod; diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java index 49083df0..89b02f01 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-2020 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,6 +28,8 @@ import lombok.Lombok; public class PatchDelegatePortal { static final String CLASS_SCOPE = "org.eclipse.jdt.internal.compiler.lookup.ClassScope"; + static final String I_JAVA_ELEMENT_ARRAY = "[Lorg.eclipse.jdt.core.IJavaElement;"; + static final String SOURCE_TYPE_ELEMENT_INFO = "org.eclipse.jdt.internal.core.SourceTypeElementInfo"; public static boolean handleDelegateForType(Object classScope) { try { @@ -51,21 +53,46 @@ public class PatchDelegatePortal { } } + public static Object[] getChildren(Object returnValue, Object javaElement) { + try { + return (Object[]) Reflection.getChildren.invoke(null, returnValue, javaElement); + } catch (NoClassDefFoundError e) { + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + return (Object[]) returnValue; + } catch (IllegalAccessException e) { + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); + } catch (NullPointerException e) { + if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { + e.initCause(Reflection.problem); + throw e; + } + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + return (Object[]) returnValue; + } + } + private static final class Reflection { public static final Method handleDelegateForType; + public static final Method getChildren; public static final Throwable problem; static { - Method m = null; + Method m = null, n = null; Throwable problem_ = null; try { m = PatchDelegate.class.getMethod("handleDelegateForType", Class.forName(CLASS_SCOPE)); + n = PatchDelegate.class.getMethod("getChildren", Class.forName(I_JAVA_ELEMENT_ARRAY), Class.forName(SOURCE_TYPE_ELEMENT_INFO)); } catch (Throwable t) { // That's problematic, but as long as no local classes are used we don't actually need it. // Better fail on local classes than crash altogether. problem_ = t; } handleDelegateForType = m; + getChildren = n; problem = problem_; } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java index 8638ff47..18e2a8db 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java @@ -24,6 +24,7 @@ package lombok.eclipse.agent; import static lombok.eclipse.handlers.EclipseHandlerUtil.createAnnotation; import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -57,6 +58,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; @@ -266,6 +268,14 @@ public class PatchExtensionMethod { Binding binding = ((NameReference)methodCall.receiver).binding; if (binding instanceof TypeBinding) skip = true; } + // It's impossible to resolve the right method without types + if (Reflection.argumentsHaveErrors != null) { + try { + if ((Boolean) Reflection.argumentsHaveErrors.get(methodCall)) skip = true; + } catch (IllegalAccessException ignore) { + // ignore + } + } if (!skip) for (Extension extension : extensions) { if (!extension.suppressBaseMethods && !(methodCall.binding instanceof ProblemMethodBinding)) continue; @@ -278,13 +288,30 @@ public class PatchExtensionMethod { List<Expression> arguments = new ArrayList<Expression>(); arguments.add(methodCall.receiver); if (methodCall.arguments != null) arguments.addAll(Arrays.asList(methodCall.arguments)); + Expression[] originalArgs = methodCall.arguments; + methodCall.arguments = arguments.toArray(new Expression[0]); + List<TypeBinding> argumentTypes = new ArrayList<TypeBinding>(); for (Expression argument : arguments) { - if (argument.resolvedType != null) argumentTypes.add(argument.resolvedType); - // TODO: Instead of just skipping nulls entirely, there is probably a 'unresolved type' placeholder. THAT is what we ought to be adding here! + TypeBinding argumentType = argument.resolvedType; + if (argumentType == null && Reflection.isFunctionalExpression(argument)) { + argumentType = Reflection.getPolyTypeBinding(argument); + } + if (argumentType == null) { + argumentType = TypeBinding.NULL; + } + argumentTypes.add(argumentType); } - Expression[] originalArgs = methodCall.arguments; - methodCall.arguments = arguments.toArray(new Expression[0]); + + // Copy generic information. This one covers a few simple cases, more complex cases are still broken + int typeVariables = extensionMethod.typeVariables.length; + if (typeVariables > 0 && methodCall.receiver.resolvedType instanceof ParameterizedTypeBinding) { + ParameterizedTypeBinding parameterizedTypeBinding = (ParameterizedTypeBinding) methodCall.receiver.resolvedType; + if (parameterizedTypeBinding.arguments != null && parameterizedTypeBinding.arguments.length == typeVariables) { + methodCall.genericTypeArguments = parameterizedTypeBinding.arguments; + } + } + MethodBinding fixedBinding = scope.getMethod(extensionMethod.declaringClass, methodCall.selector, argumentTypes.toArray(new TypeBinding[0]), methodCall); if (fixedBinding instanceof ProblemMethodBinding) { methodCall.arguments = originalArgs; @@ -292,18 +319,33 @@ public class PatchExtensionMethod { PostponedInvalidMethodError.invoke(scope.problemReporter(), methodCall, fixedBinding, scope); } } else { + // If the extension method uses varargs, the last fixed binding parameter is an array but + // the method arguments are not. Even thought we already know that the method is fine we still + // have to compare each parameter with the type of the array to support autoboxing/unboxing. + boolean isVarargs = fixedBinding.isVarargs(); for (int i = 0, iend = arguments.size(); i < iend; i++) { Expression arg = arguments.get(i); - if (fixedBinding.parameters[i].isArrayType() != arg.resolvedType.isArrayType()) break; - if (arg instanceof MessageSend) { - ((MessageSend) arg).valueCast = arg.resolvedType; + TypeBinding[] parameters = fixedBinding.parameters; + TypeBinding param; + if (isVarargs && i >= parameters.length - 1) { + // Extract the array element type for all vararg arguments + param = parameters[parameters.length - 1].leafComponentType(); + } else { + param = parameters[i]; + } + // Resolve types for lambdas + if (Reflection.isFunctionalExpression(arg)) { + arg.setExpectedType(param); + arg.resolveType(scope); } - if (!fixedBinding.parameters[i].isBaseType() && arg.resolvedType.isBaseType()) { - int id = arg.resolvedType.id; - arg.implicitConversion = TypeIds.BOXING | (id + (id << 4)); // magic see TypeIds - } else if (fixedBinding.parameters[i].isBaseType() && !arg.resolvedType.isBaseType()) { - int id = fixedBinding.parameters[i].id; - arg.implicitConversion = TypeIds.UNBOXING | (id + (id << 4)); // magic see TypeIds + if (arg.resolvedType != null) { + if (!param.isBaseType() && arg.resolvedType.isBaseType()) { + int id = arg.resolvedType.id; + arg.implicitConversion = TypeIds.BOXING | (id + (id << 4)); // magic see TypeIds + } else if (param.isBaseType() && !arg.resolvedType.isBaseType()) { + int id = parameters[i].id; + arg.implicitConversion = TypeIds.UNBOXING | (id + (id << 4)); // magic see TypeIds + } } } @@ -311,6 +353,7 @@ public class PatchExtensionMethod { methodCall.actualReceiverType = extensionMethod.declaringClass; methodCall.binding = fixedBinding; methodCall.resolvedType = methodCall.binding.returnType; + methodCall.statementEnd = methodCall.sourceEnd; if (Reflection.argumentTypes != null) { try { Reflection.argumentTypes.set(methodCall, argumentTypes.toArray(new TypeBinding[0])); @@ -356,16 +399,41 @@ public class PatchExtensionMethod { } private static final class Reflection { - public static final Field argumentTypes; + public static final Field argumentTypes = Permit.permissiveGetField(MessageSend.class, "argumentTypes"); + public static final Field argumentsHaveErrors = Permit.permissiveGetField(MessageSend.class, "argumentsHaveErrors"); + private static final Class<?> functionalExpression; + private static final Constructor<?> polyTypeBindingConstructor; static { - Field a = null; + Class<?> a = null; + Constructor<?> b = null; try { - a = Permit.getField(MessageSend.class, "argumentTypes"); - } catch (Throwable t) { - //ignore - old eclipse versions don't know this one + a = Class.forName("org.eclipse.jdt.internal.compiler.ast.FunctionalExpression"); + } catch (Exception e) { + // Ignore + } + try { + b = Permit.getConstructor(Class.forName("org.eclipse.jdt.internal.compiler.lookup.PolyTypeBinding"), Expression.class); + } catch (Exception e) { + // Ignore + } + functionalExpression = a; + polyTypeBindingConstructor = b; + } + + public static boolean isFunctionalExpression(Expression expression) { + if (functionalExpression == null) return false; + return functionalExpression.isInstance(expression); + } + + public static TypeBinding getPolyTypeBinding(Expression expression) { + if (polyTypeBindingConstructor == null) return null; + try { + return (TypeBinding) polyTypeBindingConstructor.newInstance(expression); + } catch (Exception e) { + // Ignore } - argumentTypes = a; + return null; } } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java index 085c903f..08a42d1c 100755 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java @@ -65,13 +65,14 @@ public class PatchExtensionMethodCompletionProposal { CompletionProposalCollector completionProposalCollector) { List<IJavaCompletionProposal> proposals = new ArrayList<IJavaCompletionProposal>(Arrays.asList(javaCompletionProposals)); - if (canExtendCodeAssist(proposals)) { - IJavaCompletionProposal firstProposal = proposals.get(0); - int replacementOffset = getReplacementOffset(firstProposal); + if (canExtendCodeAssist()) { for (Extension extension : getExtensionMethods(completionProposalCollector)) { for (MethodBinding method : extension.extensionMethods) { - ExtensionMethodCompletionProposal newProposal = new ExtensionMethodCompletionProposal(replacementOffset); - copyNameLookupAndCompletionEngine(completionProposalCollector, firstProposal, newProposal); + if (!isMatchingProposal(method, completionProposalCollector)) { + continue; + } + ExtensionMethodCompletionProposal newProposal = new ExtensionMethodCompletionProposal(0); + copyNameLookupAndCompletionEngine(completionProposalCollector, newProposal); ASTNode node = getAssistNode(completionProposalCollector); newProposal.setMethodBinding(method, node); createAndAddJavaCompletionProposal(completionProposalCollector, newProposal, proposals); @@ -81,7 +82,6 @@ public class PatchExtensionMethodCompletionProposal { return proposals.toArray(new IJavaCompletionProposal[0]); } - private static List<Extension> getExtensionMethods(CompletionProposalCollector completionProposalCollector) { List<Extension> extensions = new ArrayList<Extension>(); ClassScope classScope = getClassScope(completionProposalCollector); @@ -96,6 +96,17 @@ public class PatchExtensionMethodCompletionProposal { return extensions; } + private static boolean isMatchingProposal(MethodBinding method, CompletionProposalCollector completionProposalCollector) { + try { + InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); + String searchToken = new String(context.getToken()); + String extensionMethodName = new String(method.selector); + return extensionMethodName.contains(searchToken); + } catch (IllegalAccessException e) { + return true; + } + } + static TypeBinding getFirstParameterType(TypeDeclaration decl, CompletionProposalCollector completionProposalCollector) { TypeBinding firstParameterType = null; ASTNode node = getAssistNode(completionProposalCollector); @@ -149,8 +160,7 @@ public class PatchExtensionMethodCompletionProposal { return scope; } - private static void copyNameLookupAndCompletionEngine(CompletionProposalCollector completionProposalCollector, IJavaCompletionProposal proposal, - InternalCompletionProposal newProposal) { + private static void copyNameLookupAndCompletionEngine(CompletionProposalCollector completionProposalCollector, InternalCompletionProposal newProposal) { try { InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); @@ -173,17 +183,10 @@ public class PatchExtensionMethodCompletionProposal { } } - private static boolean canExtendCodeAssist(List<IJavaCompletionProposal> proposals) { - return !proposals.isEmpty() && Reflection.isComplete(); - } - - private static int getReplacementOffset(Object proposal) { - try { - return Reflection.replacementOffsetField.getInt(proposal); - } catch (Exception ignore) { - return 0; - } + private static boolean canExtendCodeAssist() { + return Reflection.isComplete(); } + static class Reflection { public static final Field replacementOffsetField; diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java b/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java new file mode 100644 index 00000000..a91e4d8b --- /dev/null +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.eclipse.agent; + +import static lombok.eclipse.EclipseAugments.CompilationUnit_javadoc; + +import java.lang.reflect.Method; +import java.util.Map; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.core.CompilationUnit; +import org.eclipse.jdt.internal.core.SourceMethod; +import org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2; + +import lombok.eclipse.EclipseAugments; +import lombok.eclipse.handlers.EclipseHandlerUtil; +import lombok.permit.Permit; + +public class PatchJavadoc { + + public static String getHTMLContentFromSource(String original, IJavaElement member) { + if (original != null) { + return original; + } + + if (member instanceof SourceMethod) { + SourceMethod sourceMethod = (SourceMethod) member; + ICompilationUnit iCompilationUnit = sourceMethod.getCompilationUnit(); + if (iCompilationUnit instanceof CompilationUnit) { + CompilationUnit compilationUnit = (CompilationUnit) iCompilationUnit; + Map<String, String> docs = EclipseAugments.CompilationUnit_javadoc.get(compilationUnit); + + String signature = getSignature(sourceMethod); + String rawJavadoc = docs.get(signature); + if (rawJavadoc == null) return null; + + return Reflection.javadoc2HTML((IMember) member, member, rawJavadoc); + } + } + + return null; + } + + public static StringBuffer printMethod(AbstractMethodDeclaration methodDeclaration, Integer tab, StringBuffer output, TypeDeclaration type) { + if (methodDeclaration.compilationResult.compilationUnit instanceof CompilationUnit) { + Map<String, String> docs = CompilationUnit_javadoc.get((CompilationUnit) methodDeclaration.compilationResult.compilationUnit); + if (docs != null) { + String signature = EclipseHandlerUtil.getSignature(type, methodDeclaration); + String rawJavadoc = docs.get(signature); + if (rawJavadoc != null) { + for (String line : rawJavadoc.split("\r?\n")) { + ASTNode.printIndent(tab, output).append(line).append("\n"); + } + } + } + } + return methodDeclaration.print(tab, output); + } + + private static String getSignature(SourceMethod sourceMethod) { + StringBuilder sb = new StringBuilder(); + sb.append(sourceMethod.getParent().getElementName()); + sb.append("."); + sb.append(sourceMethod.getElementName()); + sb.append("("); + for (String type : sourceMethod.getParameterTypes()) { + sb.append(type); + } + sb.append(")"); + + return sb.toString(); + } + + /** + * The method <code>javadoc2HTML</code> changed 2014-12 to accept an + * additional IJavaElement parameter. To support older versions, try to + * find that one too. + */ + private static class Reflection { + private static final Method javadoc2HTML; + private static final Method oldJavadoc2HTML; + static { + Method a = null, b = null; + + try { + a = Permit.getMethod(JavadocContentAccess2.class, "javadoc2HTML", IMember.class, IJavaElement.class, String.class); + } catch (Throwable t) {} + try { + b = Permit.getMethod(JavadocContentAccess2.class, "javadoc2HTML", IMember.class, String.class); + } catch (Throwable t) {} + + javadoc2HTML = a; + oldJavadoc2HTML = b; + } + + private static String javadoc2HTML(IMember member, IJavaElement element, String rawJavadoc) { + if (javadoc2HTML != null) { + try { + return (String) javadoc2HTML.invoke(null, member, element, rawJavadoc); + } catch (Throwable t) { + return null; + } + } + if (oldJavadoc2HTML != null) { + try { + return (String) oldJavadoc2HTML.invoke(null, member, rawJavadoc); + } catch (Throwable t) { + return null; + } + } + return null; + } + } +} diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index 056852c8..f22e78a8 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -35,11 +35,13 @@ import org.eclipse.jdt.internal.compiler.ast.FunctionalExpression; import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; @@ -227,14 +229,16 @@ public class PatchVal { boolean var = isVar(local, scope); if (!(val || var)) return false; - StackTraceElement[] st = new Throwable().getStackTrace(); - for (int i = 0; i < st.length - 2 && i < 10; i++) { - if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) { - boolean valInForStatement = val && - st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && - st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement"); - if (valInForStatement) return false; - break; + if (val) { + StackTraceElement[] st = new Throwable().getStackTrace(); + for (int i = 0; i < st.length - 2 && i < 10; i++) { + if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) { + boolean valInForStatement = + st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && + st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement"); + if (valInForStatement) return false; + break; + } } } @@ -264,6 +268,7 @@ public class PatchVal { } TypeBinding resolved = null; + Constant oldConstant = init.constant; try { resolved = decomponent ? getForEachComponentType(init, scope) : resolveForExpression(init, scope); } catch (NullPointerException e) { @@ -280,6 +285,10 @@ public class PatchVal { } catch (Exception e) { // Some type thing failed. } + } else { + if (init instanceof MessageSend && ((MessageSend) init).actualReceiverType == null) { + init.constant = oldConstant; + } } } diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java index 64e90db5..a2cda66c 100755 --- a/src/eclipseAgent/lombok/launch/PatchFixesHider.java +++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java @@ -35,12 +35,13 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IAnnotatable; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.dom.ArrayType; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -51,6 +52,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.MessageSend; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; @@ -192,15 +194,21 @@ final class PatchFixesHider { /** Contains patch code to support {@code @Delegate} */ public static final class Delegate { private static final Method HANDLE_DELEGATE_FOR_TYPE; + private static final Method GET_CHILDREN; static { Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchDelegatePortal"); HANDLE_DELEGATE_FOR_TYPE = Util.findMethod(shadowed, "handleDelegateForType", Object.class); + GET_CHILDREN = Util.findMethod(shadowed, "getChildren", Object.class, Object.class); } public static boolean handleDelegateForType(Object classScope) { return (Boolean) Util.invokeMethod(HANDLE_DELEGATE_FOR_TYPE, classScope); } + + public static Object[] getChildren(Object returnValue, Object javaElement) { + return (Object[]) Util.invokeMethod(GET_CHILDREN, returnValue, javaElement); + } } /** Contains patch code to support {@code val} (eclipse specific) */ @@ -304,6 +312,26 @@ final class PatchFixesHider { } } + /** Contains patch code to support Javadoc for generated methods */ + public static final class Javadoc { + private static final Method GET_HTML; + private static final Method PRINT_METHOD; + + static { + Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchJavadoc"); + GET_HTML = Util.findMethod(shadowed, "getHTMLContentFromSource", String.class, IJavaElement.class); + PRINT_METHOD = Util.findMethod(shadowed, "printMethod", AbstractMethodDeclaration.class, Integer.class, StringBuffer.class, TypeDeclaration.class); + } + + public static String getHTMLContentFromSource(String original, IJavaElement member) { + return (String) Util.invokeMethod(GET_HTML, original, member); + } + + public static StringBuffer printMethod(AbstractMethodDeclaration methodDeclaration, int tab, StringBuffer output, TypeDeclaration type) { + return (StringBuffer) Util.invokeMethod(PRINT_METHOD, methodDeclaration, tab, output, type); + } + } + /** * Contains a mix of methods: ecj only, ecj+eclipse, and eclipse only. As a consequence, _EVERY_ method from here used for ecj MUST be * transplanted, as ecj itself cannot load this class (signatures refer to things that don't exist in ecj-only mode). @@ -504,6 +532,10 @@ final class PatchFixesHider { return original == -1 ? end : original; } + public static int fixRetrieveStartBlockPosition(int original, int start) { + return original == -1 ? start : original; + } + public static int fixRetrieveRightBraceOrSemiColonPosition(int original, int end) { // if (original == -1) { // Thread.dumpStack(); @@ -525,9 +557,9 @@ final class PatchFixesHider { return -1; } - public static int fixRetrieveProperRightBracketPosition(int retVal, ArrayType arrayType) { - if (retVal != -1 || arrayType == null) return retVal; - if (isGenerated(arrayType)) return arrayType.getStartPosition() + arrayType.getLength() - 1; + public static int fixRetrieveProperRightBracketPosition(int retVal, Type type) { + if (retVal != -1 || type == null) return retVal; + if (isGenerated(type)) return type.getStartPosition() + type.getLength() - 1; return -1; } diff --git a/src/stubs/com/sun/tools/javac/code/Symbol.java b/src/stubs/com/sun/tools/javac/code/Symbol.java index 7324cb8e..15b04148 100644 --- a/src/stubs/com/sun/tools/javac/code/Symbol.java +++ b/src/stubs/com/sun/tools/javac/code/Symbol.java @@ -24,7 +24,7 @@ import com.sun.tools.javac.util.Name; public abstract class Symbol implements Element { public Type type; public Name name; - + public long flags() { return 0; } public boolean isStatic() { return false; } public boolean isConstructor() { return false; } @@ -38,7 +38,9 @@ public abstract class Symbol implements Element { @Override public Name getSimpleName() { return null; } @Override public java.util.List<Symbol> getEnclosedElements() { return null; } @Override public Element getEnclosingElement() { return null; } - + public void appendAttributes(List<Attribute.Compound> l) { + } + public static abstract class TypeSymbol extends Symbol {} public static class MethodSymbol extends Symbol implements ExecutableElement { @@ -60,6 +62,8 @@ public abstract class Symbol implements Element { public static class VarSymbol extends Symbol implements VariableElement { public Type type; + public VarSymbol(long flags, Name name, Type type, Symbol owner) { + } @Override public ElementKind getKind() { return null; } @Override public Set<Modifier> getModifiers() { return null; } @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return null; } diff --git a/src/stubs/com/sun/tools/javac/code/Symtab.java b/src/stubs/com/sun/tools/javac/code/Symtab.java index 2b524e4c..8d823531 100644 --- a/src/stubs/com/sun/tools/javac/code/Symtab.java +++ b/src/stubs/com/sun/tools/javac/code/Symtab.java @@ -5,6 +5,7 @@ package com.sun.tools.javac.code; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.ModuleSymbol; +import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.util.Context; public class Symtab { @@ -14,6 +15,7 @@ public class Symtab { public Type objectType; public static Symtab instance(Context context) {return null;} public Type unknownType; + public TypeSymbol noSymbol; // JDK 9 public ModuleSymbol unnamedModule; diff --git a/src/support/lombok/eclipseCreate/CreateEclipseDebugTarget.java b/src/support/lombok/eclipseCreate/CreateEclipseDebugTarget.java index e5e48c49..f54a5988 100644 --- a/src/support/lombok/eclipseCreate/CreateEclipseDebugTarget.java +++ b/src/support/lombok/eclipseCreate/CreateEclipseDebugTarget.java @@ -95,10 +95,12 @@ public class CreateEclipseDebugTarget { String bootpath = getBootPath(); - launchContent.append("\t\t<listEntry value=\"<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/lombok/bin" path="3" type="2"/> \"/>\n"); + launchContent.append("\t\t<listEntry value=\"<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/lombok/bin/main" path="3" type="2"/> \"/>\n"); for (Map.Entry<String, String> entry : args.entrySet()) { if (!entry.getKey().startsWith("conf.")) continue; - String[] files = entry.getValue().split(Pattern.quote(File.pathSeparator)); + String v = entry.getValue(); + if (v.equals("NONE")) continue; + String[] files = v.split(Pattern.quote(File.pathSeparator)); for (String file : files) { String n; try { @@ -128,7 +130,7 @@ public class CreateEclipseDebugTarget { launchContent.append("\t<listAttribute key=\"org.eclipse.jdt.launching.MODULEPATH\"/>\n"); launchContent.append("\t<stringAttribute key=\"org.eclipse.jdt.launching.PROJECT_ATTR\" value=\"lombok\"/>\n"); if (getArgBoolean("shadowLoaderBased")) { - launchContent.append("<stringAttribute key=\"org.eclipse.jdt.launching.VM_ARGUMENTS\" value=\"-javaagent:dist/lombok.jar -Dshadow.override.lombok=${project_loc:lombok}/bin"); + launchContent.append("<stringAttribute key=\"org.eclipse.jdt.launching.VM_ARGUMENTS\" value=\"-javaagent:dist/lombok.jar -Dshadow.override.lombok=${project_loc:lombok}/bin/main"); for (Map.Entry<String, String> entry : args.entrySet()) { if (!entry.getKey().startsWith("conf.")) continue; launchContent.append(File.pathSeparator).append(entry.getValue()); diff --git a/src/support/lombok/website/RunSite.java b/src/support/lombok/website/RunSite.java new file mode 100644 index 00000000..17e158c0 --- /dev/null +++ b/src/support/lombok/website/RunSite.java @@ -0,0 +1,103 @@ +package lombok.website; + +import static spark.Spark.*; + +import java.awt.Desktop; +import java.io.IOException; +import java.net.URI; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import spark.Request; +import spark.Response; +import spark.Route; + +public class RunSite { + private static final int DEFAULT_PORT = 4569; + private final Path base; + + public RunSite(Path base) { + this.base = base; + } + + public static void main(String[] args) throws Exception { + boolean open = args.length > 1 && args[1].equals("open"); + new RunSite(Paths.get(args[0])).go(open); + } + + private void go(boolean open) throws Exception { + port(DEFAULT_PORT); + get("/", serve("main.html")); + get("/setup/overview", serve("setup/main.html")); + get("/setup", serve("setup/main.html")); + get("/features", serve("features/index.html")); + get("/features/all", serve("features/index.html")); + get("/features/experimental/all", serve("features/experimental/index.html")); + get("/features/experimental", serve("features/experimental/index.html")); + + serveDir("/", base); + + System.out.println("Serving page from " + base + " -- hit enter to stop"); + if (open) Opener.open("http://localhost:" + DEFAULT_PORT + "/"); + System.in.read(); + System.exit(0); + } + + private void serveDir(String sub, Path dir) throws IOException { + DirectoryStream<Path> ds = Files.newDirectoryStream(dir); + try { + for (Path c : ds) { + String n = c.getFileName().toString(); + if (n.equals(".") || n.equals("..")) continue; + if (Files.isDirectory(c)) { + serveDir(sub + n + "/", c); + continue; + } + String rel = base.relativize(c).toString(); + get(sub + n, serve(rel)); + if (n.endsWith(".html")) get(sub + n.substring(0, n.length() - 5), serve(rel)); + } + } finally { + ds.close(); + } + } + + private static class Opener { + public static void open(String url) throws Exception { + Desktop.getDesktop().browse(new URI(url)); + } + } + + private Route serve(final String path) { + final Path tgt = base.resolve(path); + + return new Route() { + @Override public Object handle(Request req, Response res) throws Exception { + res.type(mapMime(path)); + return Files.readAllBytes(tgt); + } + }; + } + + private String mapMime(String path) { + if (path.endsWith(".css")) return "text/css; charset=UTF-8"; + if (path.endsWith(".js")) return "text/javascript; charset=UTF-8"; + if (path.endsWith(".png")) return "image/png"; + if (path.endsWith(".gif")) return "image/gif"; + if (path.endsWith(".jpg")) return "image/jpeg"; + if (path.endsWith(".mp4")) return "video/mp4"; + if (path.endsWith(".m4v")) return "video/mp4"; + if (path.endsWith(".ogv")) return "video/ogg"; + if (path.endsWith(".webm")) return "video/webm"; + if (path.endsWith(".ico")) return "image/x-icon"; + if (path.endsWith(".pdf")) return "application/pdf"; + if (path.endsWith(".json")) return "application/json"; + if (path.endsWith(".xml")) return "text/xml"; + if (path.endsWith(".woff")) return "font/woff"; + if (path.endsWith(".woff2")) return "font/woff2"; + if (path.endsWith(".html")) return "text/html; charset=UTF-8"; + return "text/plain; charset=UTF-8"; + } +} diff --git a/test/configuration/src/lombok/core/configuration/TestConfiguration.java b/test/configuration/src/lombok/core/configuration/TestConfiguration.java index 3032daf3..504c36b2 100644 --- a/test/configuration/src/lombok/core/configuration/TestConfiguration.java +++ b/test/configuration/src/lombok/core/configuration/TestConfiguration.java @@ -65,8 +65,8 @@ public class TestConfiguration { outStream.flush(); errStream.flush(); - String out = new String(rawOut.toByteArray()).replace("\r\n", "\n").replace('\\', '/').replaceAll(Pattern.quote(normalizedName) + "|" + Pattern.quote(baseName), "BASE/").trim(); - String err = new String(rawErr.toByteArray()).replace("\r\n", "\n").replace('\\', '/').replaceAll(Pattern.quote(normalizedName) + "|" + Pattern.quote(baseName), "BASE/").trim(); + String out = new String(rawOut.toByteArray()).replace('\\', '/').replaceAll(Pattern.quote(normalizedName) + "|" + Pattern.quote(baseName), "BASE/").trim(); + String err = new String(rawErr.toByteArray()).replace('\\', '/').replaceAll(Pattern.quote(normalizedName) + "|" + Pattern.quote(baseName), "BASE/").trim(); checkContent(directory, out, "out"); checkContent(directory, err, "err"); diff --git a/test/core/src/lombok/AbstractRunTests.java b/test/core/src/lombok/AbstractRunTests.java index 8e73e122..448f77ab 100644 --- a/test/core/src/lombok/AbstractRunTests.java +++ b/test/core/src/lombok/AbstractRunTests.java @@ -285,7 +285,7 @@ public abstract class AbstractRunTests { endIdx--; } - return in.substring(0, endIdx); + return in.substring(0, endIdx + 1); } private static String[] removeBlanks(String[] in) { diff --git a/test/core/src/lombok/LombokTestSource.java b/test/core/src/lombok/LombokTestSource.java index 57a32333..a0a6407a 100644 --- a/test/core/src/lombok/LombokTestSource.java +++ b/test/core/src/lombok/LombokTestSource.java @@ -113,10 +113,10 @@ public class LombokTestSource { return formatPreferences; } - private static final Pattern VERSION_STYLE_1 = Pattern.compile("^(\\d+)$"); - private static final Pattern VERSION_STYLE_2 = Pattern.compile("^\\:(\\d+)$"); - private static final Pattern VERSION_STYLE_3 = Pattern.compile("^(\\d+):$"); - private static final Pattern VERSION_STYLE_4 = Pattern.compile("^(\\d+):(\\d+)$"); + private static final Pattern VERSION_STYLE_1 = Pattern.compile("^(\\d+)(?:\\s+.*)?$"); + private static final Pattern VERSION_STYLE_2 = Pattern.compile("^\\:(\\d+)(?:\\s+.*)?$"); + private static final Pattern VERSION_STYLE_3 = Pattern.compile("^(\\d+):(?:\\s+.*)?$"); + private static final Pattern VERSION_STYLE_4 = Pattern.compile("^(\\d+):(\\d+)(?:\\s+.*)?$"); private int[] parseVersionLimit(String spec) { /* Single version: '5' */ { diff --git a/test/core/src/lombok/RunTestsViaEcj.java b/test/core/src/lombok/RunTestsViaEcj.java index ab28cb0c..b98c19b7 100644 --- a/test/core/src/lombok/RunTestsViaEcj.java +++ b/test/core/src/lombok/RunTestsViaEcj.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Project Lombok Authors. + * Copyright (C) 2010-2020 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,13 +35,16 @@ import java.util.concurrent.atomic.AtomicReference; import lombok.eclipse.Eclipse; import lombok.javac.CapturingDiagnosticListener.CompilerMessage; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.batch.CompilationUnit; import org.eclipse.jdt.internal.compiler.batch.FileSystem; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; @@ -103,7 +106,8 @@ public class RunTestsViaEcj extends AbstractRunTests { }; String source = readFile(file); - final CompilationUnit sourceUnit = new CompilationUnit(source.toCharArray(), file.getName(), encoding == null ? "UTF-8" : encoding); + char[] sourceArray = source.toCharArray(); + final ICompilationUnit sourceUnit = new TestCompilationUnit(file.getName(), source); Compiler ecjCompiler = new Compiler(createFileSystem(file, minVersion), ecjErrorHandlingPolicy(), ecjCompilerOptions(), bitbucketRequestor, new DefaultProblemFactory(Locale.ENGLISH)) { @Override protected synchronized void addCompilationUnit(ICompilationUnit inUnit, CompilationUnitDeclaration parsedUnit) { @@ -126,11 +130,40 @@ public class RunTestsViaEcj extends AbstractRunTests { if (cud == null) result.append("---- No CompilationUnit provided by ecj ----"); else result.append(cud.toString()); + if (eclipseAvailable()) { + EclipseDomConversion.toDomAst(cud, sourceArray); + } + + return true; + } + + private boolean eclipseAvailable() { + try { + Class.forName("org.eclipse.jdt.core.dom.CompilationUnit"); + } catch (Throwable t) { + return false; + } + return true; } private static final String bootRuntimePath = System.getProperty("delombok.bootclasspath"); + private static class EclipseDomConversion { + static CompilationUnit toDomAst(CompilationUnitDeclaration cud, final char[] source) { + Map<String, String> options = new HashMap<String, String>(); + options.put(JavaCore.COMPILER_SOURCE, "11"); + options.put("org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures", "enabled"); + + org.eclipse.jdt.internal.core.CompilationUnit ccu = new org.eclipse.jdt.internal.core.CompilationUnit(null, null, null) { + @Override public char[] getContents() { + return source; + } + }; + return AST.convertCompilationUnit(4, cud, options, false, ccu, 0, null); + } + } + private FileSystem createFileSystem(File file, int minVersion) { List<String> classpath = new ArrayList<String>(); for (Iterator<String> i = classpath.iterator(); i.hasNext();) { @@ -150,4 +183,42 @@ public class RunTestsViaEcj extends AbstractRunTests { } return new FileSystem(classpath.toArray(new String[0]), new String[] {file.getAbsolutePath()}, "UTF-8"); } + + private static final class TestCompilationUnit extends org.eclipse.jdt.internal.core.CompilationUnit { + private final char[] source; + private final char[] mainTypeName; + + private TestCompilationUnit(String name, String source) { + super(null, name, null); + this.source = source.toCharArray(); + + char[] fileNameCharArray = getFileName(); + int start = CharOperation.lastIndexOf(File.separatorChar, fileNameCharArray) + 1; + int end = CharOperation.lastIndexOf('.', fileNameCharArray); + if (end == -1) { + end = fileNameCharArray.length; + } + mainTypeName = CharOperation.subarray(fileNameCharArray, start, end); + } + + @Override public char[] getContents() { + return source; + } + + @Override public char[] getMainTypeName() { + return mainTypeName; + } + + @Override public boolean ignoreOptionalProblems() { + return false; + } + + @Override public char[][] getPackageName() { + return null; + } + + @Override public char[] getModuleName() { + return null; + } + } } diff --git a/test/pretty/resource/before/ThisParameter.java b/test/pretty/resource/before/ThisParameter.java index d95c0261..e37651cb 100644 --- a/test/pretty/resource/before/ThisParameter.java +++ b/test/pretty/resource/before/ThisParameter.java @@ -1,3 +1,4 @@ +// version 9: the 'this' param option exists in java8, but is bugged, in that annotations are not allowed on them, even without a @Target. The only purpose of the this param is annotations, so, boy, isn't that a punch in the face? import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/test/transform/resource/after-delombok/DelegateWithVarargs2.java b/test/transform/resource/after-delombok/DelegateWithVarargs2.java new file mode 100644 index 00000000..a8ff6e3b --- /dev/null +++ b/test/transform/resource/after-delombok/DelegateWithVarargs2.java @@ -0,0 +1,12 @@ +class DelegateWithVarargs2 { + private DelegateWithVarargs2.B bar; + public class B { + public void varargs(Object[]... keys) { + } + } + @java.lang.SuppressWarnings("all") + public void varargs(final java.lang.Object[]... keys) { + this.bar.varargs(keys); + } +} + diff --git a/test/transform/resource/after-delombok/ExtensionMethodAutoboxing.java b/test/transform/resource/after-delombok/ExtensionMethodAutoboxing.java new file mode 100644 index 00000000..f274cabb --- /dev/null +++ b/test/transform/resource/after-delombok/ExtensionMethodAutoboxing.java @@ -0,0 +1,20 @@ +class ExtensionMethodAutoboxing { + public void test() { + Long l1 = 1L; + long l2 = 1L; + Integer i1 = 1; + int i2 = 1; + String string = "test"; + ExtensionMethodAutoboxing.Extensions.boxing(string, l1, i1); + ExtensionMethodAutoboxing.Extensions.boxing(string, l1, i2); + ExtensionMethodAutoboxing.Extensions.boxing(string, l2, i1); + ExtensionMethodAutoboxing.Extensions.boxing(string, l2, i2); + } + + + static class Extensions { + public static String boxing(String string, Long a, int b) { + return string + " " + a + " " + b; + } + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-delombok/ExtensionMethodFunctional.java b/test/transform/resource/after-delombok/ExtensionMethodFunctional.java new file mode 100644 index 00000000..bd301543 --- /dev/null +++ b/test/transform/resource/after-delombok/ExtensionMethodFunctional.java @@ -0,0 +1,28 @@ +import java.util.function.Function; +import java.util.function.Consumer; + +class ExtensionMethodFunctional { + public void test() { + String test = "test"; + test = ExtensionMethodFunctional.Extensions.map(test, s -> ExtensionMethodFunctional.Extensions.reverse(s)); + ExtensionMethodFunctional.Extensions.consume(test, s -> System.out.println("1: " + s), s -> System.out.println("2: " + s)); + ExtensionMethodFunctional.Extensions.consume(test, System.out::println, System.out::println); + } + + static class Extensions { + public static <T, R> R map(T value, Function<T, R> mapper) { + return mapper.apply(value); + } + + public static String reverse(String string) { + return new StringBuilder(string).reverse().toString(); + } + + @SafeVarargs + public static <T> void consume(T o, Consumer<T>... consumer) { + for (int i = 0; i < consumer.length; i++) { + consumer[i].accept(o); + } + } + } +} diff --git a/test/transform/resource/after-delombok/ExtensionMethodVarargs.java b/test/transform/resource/after-delombok/ExtensionMethodVarargs.java new file mode 100644 index 00000000..237b73ef --- /dev/null +++ b/test/transform/resource/after-delombok/ExtensionMethodVarargs.java @@ -0,0 +1,19 @@ +class ExtensionMethodVarargs { + public void test() { + Long l1 = 1L; + long l2 = 1L; + Integer i1 = 1; + int i2 = 1; + ExtensionMethodVarargs.Extensions.format("%d %d %d %d", l1, l2, i1, i2); + ExtensionMethodVarargs.Extensions.format("%d", l1); + ExtensionMethodVarargs.Extensions.format("", new Integer[] {1, 2}); + ExtensionMethodVarargs.Extensions.format("", new Integer[] {1, 2}, new Integer[] {1, 2}); + } + + + static class Extensions { + public static String format(String string, Object... params) { + return String.format(string, params); + } + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-delombok/GetterSetterJavadoc.java b/test/transform/resource/after-delombok/GetterSetterJavadoc.java index ae662da7..78d120a4 100644 --- a/test/transform/resource/after-delombok/GetterSetterJavadoc.java +++ b/test/transform/resource/after-delombok/GetterSetterJavadoc.java @@ -115,7 +115,7 @@ class GetterSetterJavadoc4 { /** * Some text * - * @param fieldName Hello, World5 + * @param fieldName Hello, World4 * @return {@code this}. */ @java.lang.SuppressWarnings("all") diff --git a/test/transform/resource/after-delombok/LoggerSlf4j.java b/test/transform/resource/after-delombok/LoggerSlf4j.java index 70f11ae4..152c8708 100644 --- a/test/transform/resource/after-delombok/LoggerSlf4j.java +++ b/test/transform/resource/after-delombok/LoggerSlf4j.java @@ -29,8 +29,3 @@ class LoggerSlf4jWithTwoStaticFields { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LoggerSlf4jWithTwoStaticFields.TOPIC + LoggerSlf4jWithTwoStaticFields.TOPIC); static final String TOPIC = "StaticField"; } - -class LoggerSlf4jWithTwoLiterals { - @java.lang.SuppressWarnings("all") - private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger("A" + "B"); -}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/BuilderJavadoc.java b/test/transform/resource/after-ecj/BuilderJavadoc.java index 445b5417..3afd3be2 100644 --- a/test/transform/resource/after-ecj/BuilderJavadoc.java +++ b/test/transform/resource/after-ecj/BuilderJavadoc.java @@ -16,10 +16,21 @@ import java.util.List; @java.lang.SuppressWarnings("all") BuilderJavadocBuilder() { super(); } + /** + * basic gets only a builder setter. + * @see #getsetwith + * @param tag is moved to the setter. + * @return {@code this}. + */ public @java.lang.SuppressWarnings("all") BuilderJavadoc.BuilderJavadocBuilder<T> basic(final int basic) { this.basic = basic; return this; } + /** + * getsetwith gets a builder setter, an instance getter and setter, and a wither. + * @param tag is moved to the setters and wither. + * @return {@code this}. + */ public @java.lang.SuppressWarnings("all") BuilderJavadoc.BuilderJavadocBuilder<T> getsetwith(final int getsetwith) { this.getsetwith = getsetwith; return this; @@ -45,12 +56,26 @@ import java.util.List; public static @java.lang.SuppressWarnings("all") <T>BuilderJavadoc.BuilderJavadocBuilder<T> builder() { return new BuilderJavadoc.BuilderJavadocBuilder<T>(); } + /** + * getsetwith gets a builder setter, an instance getter and setter, and a wither. + * + * @return tag is moved to the getter. + */ public @java.lang.SuppressWarnings("all") int getGetsetwith() { return this.getsetwith; } + /** + * getsetwith gets a builder setter, an instance getter and setter, and a wither. + * @param tag is moved to the setters and wither. + */ public @java.lang.SuppressWarnings("all") void setGetsetwith(final int getsetwith) { this.getsetwith = getsetwith; } + /** + * getsetwith gets a builder setter, an instance getter and setter, and a wither. + * @param tag is moved to the setters and wither. + * @return a clone of this object, except with this updated property (returns {@code this} if an identical value is passed). + */ public @java.lang.SuppressWarnings("all") BuilderJavadoc<T> withGetsetwith(final int getsetwith) { return ((this.getsetwith == getsetwith) ? this : new BuilderJavadoc<T>(this.basic, getsetwith, this.predef, this.predefWithJavadoc)); } diff --git a/test/transform/resource/after-ecj/BuilderWithDeprecated.java b/test/transform/resource/after-ecj/BuilderWithDeprecated.java index 724a25e6..65326a66 100644 --- a/test/transform/resource/after-ecj/BuilderWithDeprecated.java +++ b/test/transform/resource/after-ecj/BuilderWithDeprecated.java @@ -10,6 +10,10 @@ public @Builder class BuilderWithDeprecated { @java.lang.SuppressWarnings("all") BuilderWithDeprecatedBuilder() { super(); } + /** + * @deprecated since always + * @return {@code this}. + */ public @java.lang.Deprecated @java.lang.SuppressWarnings("all") BuilderWithDeprecated.BuilderWithDeprecatedBuilder dep1(final String dep1) { this.dep1 = dep1; return this; diff --git a/test/transform/resource/after-ecj/DelegateWithVarargs2.java b/test/transform/resource/after-ecj/DelegateWithVarargs2.java new file mode 100644 index 00000000..ed0cddf5 --- /dev/null +++ b/test/transform/resource/after-ecj/DelegateWithVarargs2.java @@ -0,0 +1,17 @@ +import lombok.experimental.Delegate; +class DelegateWithVarargs2 { + public class B { + public B() { + super(); + } + public void varargs(Object[]... keys) { + } + } + private @Delegate DelegateWithVarargs2.B bar; + DelegateWithVarargs2() { + super(); + } + public @java.lang.SuppressWarnings("all") void varargs(final java.lang.Object[]... keys) { + this.bar.varargs(keys); + } +} diff --git a/test/transform/resource/after-ecj/ExtensionMethodAutoboxing.java b/test/transform/resource/after-ecj/ExtensionMethodAutoboxing.java new file mode 100644 index 00000000..8bc2b30f --- /dev/null +++ b/test/transform/resource/after-ecj/ExtensionMethodAutoboxing.java @@ -0,0 +1,25 @@ +import lombok.experimental.ExtensionMethod; +@ExtensionMethod({ExtensionMethodAutoboxing.Extensions.class}) class ExtensionMethodAutoboxing { + static class Extensions { + Extensions() { + super(); + } + public static String boxing(String string, Long a, int b) { + return ((((string + " ") + a) + " ") + b); + } + } + ExtensionMethodAutoboxing() { + super(); + } + public void test() { + Long l1 = 1l; + long l2 = 1l; + Integer i1 = 1; + int i2 = 1; + String string = "test"; + ExtensionMethodAutoboxing.Extensions.boxing(string, l1, i1); + ExtensionMethodAutoboxing.Extensions.boxing(string, l1, i2); + ExtensionMethodAutoboxing.Extensions.boxing(string, l2, i1); + ExtensionMethodAutoboxing.Extensions.boxing(string, l2, i2); + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/ExtensionMethodFunctional.java b/test/transform/resource/after-ecj/ExtensionMethodFunctional.java new file mode 100644 index 00000000..0190eabb --- /dev/null +++ b/test/transform/resource/after-ecj/ExtensionMethodFunctional.java @@ -0,0 +1,31 @@ +import java.util.function.Function; +import java.util.function.Consumer; +import lombok.experimental.ExtensionMethod; +@ExtensionMethod(ExtensionMethodFunctional.Extensions.class) class ExtensionMethodFunctional { + static class Extensions { + Extensions() { + super(); + } + public static <T, R>R map(T value, Function<T, R> mapper) { + return mapper.apply(value); + } + public static String reverse(String string) { + return new StringBuilder(string).reverse().toString(); + } + public static @SafeVarargs <T>void consume(T o, Consumer<T>... consumer) { + for (int i = 0;; (i < consumer.length); i ++) + { + consumer[i].accept(o); + } + } + } + ExtensionMethodFunctional() { + super(); + } + public void test() { + String test = "test"; + test = ExtensionMethodFunctional.Extensions.map(test, (<no type> s) -> ExtensionMethodFunctional.Extensions.reverse(s)); + ExtensionMethodFunctional.Extensions.consume(test, (<no type> s) -> System.out.println(("1: " + s)), (<no type> s) -> System.out.println(("2: " + s))); + ExtensionMethodFunctional.Extensions.consume(test, System.out::println, System.out::println); + } +} diff --git a/test/transform/resource/after-ecj/ExtensionMethodVarargs.java b/test/transform/resource/after-ecj/ExtensionMethodVarargs.java new file mode 100644 index 00000000..727b6494 --- /dev/null +++ b/test/transform/resource/after-ecj/ExtensionMethodVarargs.java @@ -0,0 +1,24 @@ +import lombok.experimental.ExtensionMethod; +@ExtensionMethod(ExtensionMethodVarargs.Extensions.class) class ExtensionMethodVarargs { + static class Extensions { + Extensions() { + super(); + } + public static String format(String string, Object... params) { + return String.format(string, params); + } + } + ExtensionMethodVarargs() { + super(); + } + public void test() { + Long l1 = 1l; + long l2 = 1l; + Integer i1 = 1; + int i2 = 1; + ExtensionMethodVarargs.Extensions.format("%d %d %d %d", l1, l2, i1, i2); + ExtensionMethodVarargs.Extensions.format("%d", l1); + ExtensionMethodVarargs.Extensions.format("", new Integer[]{1, 2}); + ExtensionMethodVarargs.Extensions.format("", new Integer[]{1, 2}, new Integer[]{1, 2}); + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/GetterDeprecated.java b/test/transform/resource/after-ecj/GetterDeprecated.java index 546f7fb7..087a60b8 100644 --- a/test/transform/resource/after-ecj/GetterDeprecated.java +++ b/test/transform/resource/after-ecj/GetterDeprecated.java @@ -8,6 +8,9 @@ class GetterDeprecated { public @java.lang.Deprecated @java.lang.SuppressWarnings("all") int getAnnotation() { return this.annotation; } + /** + * @deprecated + */ public @java.lang.Deprecated @java.lang.SuppressWarnings("all") int getJavadoc() { return this.javadoc; } diff --git a/test/transform/resource/after-ecj/GetterSetterJavadoc.java b/test/transform/resource/after-ecj/GetterSetterJavadoc.java index 4923fd02..c3308c51 100644 --- a/test/transform/resource/after-ecj/GetterSetterJavadoc.java +++ b/test/transform/resource/after-ecj/GetterSetterJavadoc.java @@ -1,8 +1,18 @@ @lombok.Data class GetterSetterJavadoc1 { private int fieldName; + /** + * Getter section + * + * @return Sky is blue1 + */ public @java.lang.SuppressWarnings("all") int getFieldName() { return this.fieldName; } + /** + * Some text + * + * @param fieldName Hello, World1 + */ public @java.lang.SuppressWarnings("all") void setFieldName(final int fieldName) { this.fieldName = fieldName; } @@ -39,9 +49,19 @@ class GetterSetterJavadoc2 { GetterSetterJavadoc2() { super(); } + /** + * Some text + * + * @return Sky is blue2 + */ public @java.lang.SuppressWarnings("all") int getFieldName() { return this.fieldName; } + /** + * Some text + * + * @param fieldName Hello, World2 + */ public @java.lang.SuppressWarnings("all") void setFieldName(final int fieldName) { this.fieldName = fieldName; } @@ -51,9 +71,17 @@ class GetterSetterJavadoc3 { GetterSetterJavadoc3() { super(); } + /** + * Getter section + * @return Sky is blue3 + */ public @java.lang.SuppressWarnings("all") int getFieldName() { return this.fieldName; } + /** + * Setter section + * @param fieldName Hello, World3 + */ public @java.lang.SuppressWarnings("all") void setFieldName(final int fieldName) { this.fieldName = fieldName; } @@ -63,9 +91,20 @@ class GetterSetterJavadoc3 { GetterSetterJavadoc4() { super(); } + /** + * Some text + * + * @return Sky is blue4 + */ public @java.lang.SuppressWarnings("all") int fieldName() { return this.fieldName; } + /** + * Some text + * + * @param fieldName Hello, World4 + * @return {@code this}. + */ public @java.lang.SuppressWarnings("all") GetterSetterJavadoc4 fieldName(final int fieldName) { this.fieldName = fieldName; return this; @@ -76,9 +115,18 @@ class GetterSetterJavadoc3 { GetterSetterJavadoc5() { super(); } + /** + * Getter section + * @return Sky is blue5 + */ public @java.lang.SuppressWarnings("all") int fieldName() { return this.fieldName; } + /** + * Setter section + * @param fieldName Hello, World5 + * @return Sky is blue5 + */ public @java.lang.SuppressWarnings("all") GetterSetterJavadoc5 fieldName(final int fieldName) { this.fieldName = fieldName; return this; diff --git a/test/transform/resource/after-ecj/LoggerSlf4j.java b/test/transform/resource/after-ecj/LoggerSlf4j.java index c303a895..286d023b 100644 --- a/test/transform/resource/after-ecj/LoggerSlf4j.java +++ b/test/transform/resource/after-ecj/LoggerSlf4j.java @@ -56,11 +56,3 @@ class LoggerSlf4jOuter { super(); } } -@Slf4j(topic = ExtendedStringLiteral{AB}) class LoggerSlf4jWithTwoLiterals { - private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger("AB"); - <clinit>() { - } - LoggerSlf4jWithTwoLiterals() { - super(); - } -}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/SetterAndWithMethodJavadoc.java b/test/transform/resource/after-ecj/SetterAndWithMethodJavadoc.java index dd64e358..c95b7bab 100644 --- a/test/transform/resource/after-ecj/SetterAndWithMethodJavadoc.java +++ b/test/transform/resource/after-ecj/SetterAndWithMethodJavadoc.java @@ -7,15 +7,33 @@ class SetterAndWithMethodJavadoc { this.i = i; this.j = j; } + /** + * Some value. + * @param the new value + */ public @java.lang.SuppressWarnings("all") void setI(final int i) { this.i = i; } + /** + * Some value. + * @param the new value + * @return a clone of this object, except with this updated property (returns {@code this} if an identical value is passed). + */ public @java.lang.SuppressWarnings("all") SetterAndWithMethodJavadoc withI(final int i) { return ((this.i == i) ? this : new SetterAndWithMethodJavadoc(i, this.j)); } + /** + * Set some other value. + * @param the new other value + */ public @java.lang.SuppressWarnings("all") void setJ(final int j) { this.j = j; } + /** + * Reinstantiate with some other value. + * @param the other new other value + * @return a clone of this object, except with this updated property (returns {@code this} if an identical value is passed). + */ public @java.lang.SuppressWarnings("all") SetterAndWithMethodJavadoc withJ(final int j) { return ((this.j == j) ? this : new SetterAndWithMethodJavadoc(this.i, j)); } diff --git a/test/transform/resource/after-ecj/SetterDeprecated.java b/test/transform/resource/after-ecj/SetterDeprecated.java index d76612b7..cc089566 100644 --- a/test/transform/resource/after-ecj/SetterDeprecated.java +++ b/test/transform/resource/after-ecj/SetterDeprecated.java @@ -8,6 +8,9 @@ class SetterDeprecated { public @java.lang.Deprecated @java.lang.SuppressWarnings("all") void setAnnotation(final int annotation) { this.annotation = annotation; } + /** + * @deprecated + */ public @java.lang.Deprecated @java.lang.SuppressWarnings("all") void setJavadoc(final int javadoc) { this.javadoc = javadoc; } diff --git a/test/transform/resource/after-ecj/WithMethodMarkedDeprecated.java b/test/transform/resource/after-ecj/WithMethodMarkedDeprecated.java index cd123fe8..4220308c 100644 --- a/test/transform/resource/after-ecj/WithMethodMarkedDeprecated.java +++ b/test/transform/resource/after-ecj/WithMethodMarkedDeprecated.java @@ -8,6 +8,10 @@ class WithMethodMarkedDeprecated { public @java.lang.Deprecated @java.lang.SuppressWarnings("all") WithMethodMarkedDeprecated withAnnotation(final int annotation) { return ((this.annotation == annotation) ? this : new WithMethodMarkedDeprecated(annotation, this.javadoc)); } + /** + * @deprecated + * @return a clone of this object, except with this updated property (returns {@code this} if an identical value is passed). + */ public @java.lang.Deprecated @java.lang.SuppressWarnings("all") WithMethodMarkedDeprecated withJavadoc(final int javadoc) { return ((this.javadoc == javadoc) ? this : new WithMethodMarkedDeprecated(this.annotation, javadoc)); } diff --git a/test/transform/resource/before/DelegateWithVarargs2.java b/test/transform/resource/before/DelegateWithVarargs2.java new file mode 100644 index 00000000..8a3dbf14 --- /dev/null +++ b/test/transform/resource/before/DelegateWithVarargs2.java @@ -0,0 +1,9 @@ +import lombok.experimental.Delegate; + +class DelegateWithVarargs2 { + @Delegate private DelegateWithVarargs2.B bar; + + public class B { + public void varargs(Object[]... keys) {} + } +} diff --git a/test/transform/resource/before/ExtensionMethodAutoboxing.java b/test/transform/resource/before/ExtensionMethodAutoboxing.java new file mode 100644 index 00000000..5e07a6b3 --- /dev/null +++ b/test/transform/resource/before/ExtensionMethodAutoboxing.java @@ -0,0 +1,23 @@ +import lombok.experimental.ExtensionMethod; + +@ExtensionMethod({ExtensionMethodAutoboxing.Extensions.class}) +class ExtensionMethodAutoboxing { + public void test() { + Long l1 = 1l; + long l2 = 1l; + Integer i1 = 1; + int i2 = 1; + + String string = "test"; + string.boxing(l1, i1); + string.boxing(l1, i2); + string.boxing(l2, i1); + string.boxing(l2, i2); + } + + static class Extensions { + public static String boxing(String string, Long a, int b) { + return string + " " + a + " " + b; + } + } +} diff --git a/test/transform/resource/before/ExtensionMethodFunctional.java b/test/transform/resource/before/ExtensionMethodFunctional.java new file mode 100644 index 00000000..19983258 --- /dev/null +++ b/test/transform/resource/before/ExtensionMethodFunctional.java @@ -0,0 +1,33 @@ +// version 8: +import java.util.function.Function; +import java.util.function.Consumer; + +import lombok.experimental.ExtensionMethod; + +@ExtensionMethod(ExtensionMethodFunctional.Extensions.class) +class ExtensionMethodFunctional { + public void test() { + String test = "test"; + test = test.map(s -> s.reverse()); + + test.consume(s -> System.out.println("1: " + s), s -> System.out.println("2: " + s)); + test.consume(System.out::println, System.out::println); + } + + static class Extensions { + public static <T, R> R map(T value, Function<T, R> mapper) { + return mapper.apply(value); + } + + public static String reverse(String string) { + return new StringBuilder(string).reverse().toString(); + } + + @SafeVarargs + public static <T> void consume(T o, Consumer<T>... consumer) { + for (int i = 0; i < consumer.length; i++) { + consumer[i].accept(o); + } + } + } +} diff --git a/test/transform/resource/before/ExtensionMethodVarargs.java b/test/transform/resource/before/ExtensionMethodVarargs.java new file mode 100644 index 00000000..a976f9e6 --- /dev/null +++ b/test/transform/resource/before/ExtensionMethodVarargs.java @@ -0,0 +1,23 @@ +// version 8: +import lombok.experimental.ExtensionMethod; + +@ExtensionMethod(ExtensionMethodVarargs.Extensions.class) +class ExtensionMethodVarargs { + public void test() { + Long l1 = 1l; + long l2 = 1l; + Integer i1 = 1; + int i2 = 1; + + "%d %d %d %d".format(l1, l2, i1, i2); + "%d".format(l1); + "".format(new Integer[]{1,2}); + "".format(new Integer[]{1,2}, new Integer[]{1,2}); + } + + static class Extensions { + public static String format(String string, Object... params) { + return String.format(string, params); + } + } +} diff --git a/test/transform/resource/before/LoggerSlf4j.java b/test/transform/resource/before/LoggerSlf4j.java index 3f8284e8..c59db4ee 100644 --- a/test/transform/resource/before/LoggerSlf4j.java +++ b/test/transform/resource/before/LoggerSlf4j.java @@ -28,7 +28,3 @@ class LoggerSlf4jWithStaticField { class LoggerSlf4jWithTwoStaticFields { static final String TOPIC = "StaticField"; } - -@Slf4j(topic="A"+"B") -class LoggerSlf4jWithTwoLiterals { -}
\ No newline at end of file diff --git a/test/transform/resource/messages-delombok/ExtensionMethodVarargs.java.messages b/test/transform/resource/messages-delombok/ExtensionMethodVarargs.java.messages new file mode 100644 index 00000000..587d0767 --- /dev/null +++ b/test/transform/resource/messages-delombok/ExtensionMethodVarargs.java.messages @@ -0,0 +1 @@ +14 non-varargs call of varargs method with inexact argument type for last parameter
\ No newline at end of file diff --git a/test/transform/resource/messages-idempotent/ExtensionMethodVarargs.java.messages b/test/transform/resource/messages-idempotent/ExtensionMethodVarargs.java.messages new file mode 100644 index 00000000..5a1016ca --- /dev/null +++ b/test/transform/resource/messages-idempotent/ExtensionMethodVarargs.java.messages @@ -0,0 +1 @@ +9 non-varargs call of varargs method with inexact argument type for last parameter
\ No newline at end of file diff --git a/website/resources/img/eclipse-p2-step1.png b/website/resources/img/eclipse-p2-step1.png Binary files differnew file mode 100644 index 00000000..92cdef86 --- /dev/null +++ b/website/resources/img/eclipse-p2-step1.png diff --git a/website/resources/img/eclipse-p2-step2.png b/website/resources/img/eclipse-p2-step2.png Binary files differnew file mode 100644 index 00000000..84139ad8 --- /dev/null +++ b/website/resources/img/eclipse-p2-step2.png diff --git a/website/templates/features/Builder.html b/website/templates/features/Builder.html index f9897d03..1b6c6e62 100644 --- a/website/templates/features/Builder.html +++ b/website/templates/features/Builder.html @@ -132,7 +132,7 @@ </li> </ul> </p><p> - If your identifiers are written in common english, lombok assumes that the name of any collection with <code>@Singular</code> on it is an english plural and will attempt to automatically singularize that name. If this is possible, the add-one method will use this name. For example, if your collection is called <code>statuses</code>, then the add-one method will automatically be called <code>status</code>. You can also specify the singular form of your identifier explictly by passing the singular form as argument to the annotation like so: <code>@Singular("axis") List<Line> axes;</code>.<br /> + If your identifiers are written in common english, lombok assumes that the name of any collection with <code>@Singular</code> on it is an english plural and will attempt to automatically singularize that name. If this is possible, the add-one method will use this name. For example, if your collection is called <code>statuses</code>, then the add-one method will automatically be called <code>status</code>. You can also specify the singular form of your identifier explicitly by passing the singular form as argument to the annotation like so: <code>@Singular("axis") List<Line> axes;</code>.<br /> If lombok cannot singularize your identifier, or it is ambiguous, lombok will generate an error and force you to explicitly specify the singular name. </p><p> The snippet below does not show what lombok generates for a <code>@Singular</code> field/parameter because it is rather complicated. You can view a snippet <a href="builderSingular">here</a>. diff --git a/website/templates/setup/eclipse.html b/website/templates/setup/eclipse.html index 85304c83..d9621501 100644 --- a/website/templates/setup/eclipse.html +++ b/website/templates/setup/eclipse.html @@ -23,4 +23,16 @@ <img src="/img/eclipse-about.png" /> </p> </@s.introduction> + <@s.section title="Via eclipse plugin installer"> + <em>WARNING: This plugin installer is currently unsigned, and we have given up on figuring out how to fix that; if team eclipse or somebody else can help us out, we're all ears!</em> + <p> + You can install lombok directly from within eclipse, and in that way, you can also include lombok as part of your team eclipse deployment configuration. To do this, use + update site <code>https://projectlombok.org/p2</code>:<br /> + <br /> + <img width="448" height="431" src="/img/eclipse-p2-step1.png" /><br /> + <br /> + <br /> + <img width="626" height="216" src="/img/eclipse-p2-step2.png" /> + </p> + </@s.section> </@s.scaffold> diff --git a/website/templates/setup/kobalt.html b/website/templates/setup/kobalt.html index 26adf23d..beb60d8c 100644 --- a/website/templates/setup/kobalt.html +++ b/website/templates/setup/kobalt.html @@ -3,7 +3,7 @@ <@s.scaffold title="Kobalt"> <@s.introduction> <p> - To set up lombok with any build tool, you have to specify that the lombok dependency is required to compile your source code, but does not need to be present when running/testing/jarring/otherwise deploying your code. Generally this is called a 'provided' dependency. This page explains how to integrate lombok with the <a href="http://beust.com/kobalt/home/index.html">Kobalt</a> buid tool. + To set up lombok with any build tool, you have to specify that the lombok dependency is required to compile your source code, but does not need to be present when running/testing/jarring/otherwise deploying your code. Generally this is called a 'provided' dependency. This page explains how to integrate lombok with the <a href="http://beust.com/kobalt/home/index.html">Kobalt</a> build tool. </p><p> Lombok is available in maven central, so telling Kobalt to download lombok is easy. </p> |