diff options
Diffstat (limited to 'src')
157 files changed, 5282 insertions, 2653 deletions
diff --git a/src/ant/lombok/ant/SimpleTestFormatter.java b/src/ant/lombok/ant/SimpleTestFormatter.java new file mode 100644 index 00000000..a2a38420 --- /dev/null +++ b/src/ant/lombok/ant/SimpleTestFormatter.java @@ -0,0 +1,63 @@ +package lombok.ant; + +import java.io.OutputStream; +import java.io.PrintStream; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter; +import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest; + +public class SimpleTestFormatter implements JUnitResultFormatter { + private PrintStream out = System.out; + + @Override + public void addError(Test test, Throwable error) { + logResult(test, "ERR"); + out.println(error.getMessage()); + } + + @Override + public void addFailure(Test test, AssertionFailedError failure) { + logResult(test, "FAIL"); + out.println(failure.getMessage()); + } + + @Override + public void endTest(Test test) { + logResult(test, "PASS"); + } + + @Override + public void startTest(Test test) { } + + @Override + public void endTestSuite(JUnitTest testSuite) throws BuildException { } + + @Override + public void setOutput(OutputStream out) { + this.out = new PrintStream(out); + } + + @Override + public void setSystemError(String msg) { + if (msg.trim().isEmpty()) return; + out.println(msg); + } + + @Override + public void setSystemOutput(String msg) { + if (msg.trim().isEmpty()) return; + out.println(msg); + } + + @Override + public void startTestSuite(JUnitTest testSuite) throws BuildException { } + + private void logResult(Test test, String result) { + out.println("[" + result + "] " + String.valueOf(test)); + out.flush(); + } +} diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 01a4b576..ba66649b 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -274,7 +274,7 @@ public class ConfigurationKeys { * * For any class with an {@code @ToString} annotation which extends a class other than {@code java.lang.Object}, should a call to superclass's implementation of {@code toString} be included in the generated method? (Default = skip) */ - public static final ConfigurationKey<CallSuperType> TO_STRING_CALL_SUPER = new ConfigurationKey<CallSuperType>("lombok.toString.callSuper", "When generating toString for classes that extend something (other than Object), either automatically take into account superclass implementation (call), or don't (skip), or warn and don't (warn). (default = warn).") {}; + public static final ConfigurationKey<CallSuperType> TO_STRING_CALL_SUPER = new ConfigurationKey<CallSuperType>("lombok.toString.callSuper", "When generating toString for classes that extend something (other than Object), either automatically take into account superclass implementation (call), or don't (skip), or warn and don't (warn). (default = skip).") {}; /** * lombok configuration: {@code lombok.toString.flagUsage} = {@code WARNING} | {@code ERROR}. diff --git a/src/core/lombok/EqualsAndHashCode.java b/src/core/lombok/EqualsAndHashCode.java index 6805d214..279e6e3d 100644 --- a/src/core/lombok/EqualsAndHashCode.java +++ b/src/core/lombok/EqualsAndHashCode.java @@ -71,6 +71,14 @@ public @interface EqualsAndHashCode { * @return If {@code true}, always use direct field access instead of calling the getter method. */ boolean doNotUseGetters() default false; + + /** + * Determines how the result of the {@code hashCode} method will be cached. + * <strong>default: {@link CacheStrategy#NEVER}</strong> + * + * @return The {@code hashCode} cache strategy to be used. + */ + CacheStrategy cacheStrategy() default CacheStrategy.NEVER; /** * Any annotations listed here are put on the generated parameter of {@code equals} and {@code canEqual}. @@ -132,4 +140,19 @@ public @interface EqualsAndHashCode { */ int rank() default 0; } + + public enum CacheStrategy { + /** + * Never cache. Perform the calculation every time the method is called. + */ + NEVER, + /** + * Cache the result of the first invocation of {@code hashCode} and use it for subsequent invocations. + * This can improve performance if all fields used for calculating the {@code hashCode} are immutable + * and thus every invocation of {@code hashCode} will always return the same value. + * <strong>Do not use this if there's <em>any</em> chance that different invocations of {@code hashCode} + * might return different values.</strong> + */ + LAZY + } } diff --git a/src/core/lombok/bytecode/PoolConstantsApp.java b/src/core/lombok/bytecode/PoolConstantsApp.java index c2120cc0..65b51f17 100644 --- a/src/core/lombok/bytecode/PoolConstantsApp.java +++ b/src/core/lombok/bytecode/PoolConstantsApp.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,8 +26,7 @@ import java.util.ArrayList; import java.util.List; import lombok.core.LombokApp; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.zwitserloot.cmdreader.CmdReader; import com.zwitserloot.cmdreader.Description; @@ -36,9 +35,8 @@ import com.zwitserloot.cmdreader.Mandatory; import com.zwitserloot.cmdreader.Sequential; import com.zwitserloot.cmdreader.Shorthand; -@ProviderFor(LombokApp.class) +@Provides public class PoolConstantsApp extends LombokApp { - @Override public String getAppName() { return "Xprintpool"; } diff --git a/src/core/lombok/bytecode/PostCompilerApp.java b/src/core/lombok/bytecode/PostCompilerApp.java index 5f49bd81..7f2237c0 100644 --- a/src/core/lombok/bytecode/PostCompilerApp.java +++ b/src/core/lombok/bytecode/PostCompilerApp.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,8 +33,7 @@ import java.util.List; import lombok.core.DiagnosticsReceiver; import lombok.core.LombokApp; import lombok.core.PostCompiler; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.zwitserloot.cmdreader.CmdReader; import com.zwitserloot.cmdreader.Description; @@ -43,7 +42,7 @@ import com.zwitserloot.cmdreader.Mandatory; import com.zwitserloot.cmdreader.Sequential; import com.zwitserloot.cmdreader.Shorthand; -@ProviderFor(LombokApp.class) +@Provides public class PostCompilerApp extends LombokApp { @Override public List<String> getAppAliases() { return Arrays.asList("post", "postcompile"); diff --git a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java index 20572e22..14e95c46 100644 --- a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java +++ b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ import java.util.concurrent.atomic.AtomicBoolean; import lombok.core.DiagnosticsReceiver; import lombok.core.PostCompilerTransformation; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; -@ProviderFor(PostCompilerTransformation.class) +@Provides public class PreventNullAnalysisRemover implements PostCompilerTransformation { @Override public byte[] applyTransformations(byte[] original, String fileName, DiagnosticsReceiver diagnostics) { diff --git a/src/core/lombok/bytecode/SneakyThrowsRemover.java b/src/core/lombok/bytecode/SneakyThrowsRemover.java index 037cb109..7ed3f80f 100644 --- a/src/core/lombok/bytecode/SneakyThrowsRemover.java +++ b/src/core/lombok/bytecode/SneakyThrowsRemover.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,8 +27,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import lombok.core.DiagnosticsReceiver; import lombok.core.PostCompilerTransformation; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; @@ -37,7 +37,7 @@ import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; -@ProviderFor(PostCompilerTransformation.class) +@Provides public class SneakyThrowsRemover implements PostCompilerTransformation { @Override public byte[] applyTransformations(byte[] original, String fileName, final DiagnosticsReceiver diagnostics) { diff --git a/src/core/lombok/core/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java index d4a92408..28d16298 100644 --- a/src/core/lombok/core/AnnotationProcessor.java +++ b/src/core/lombok/core/AnnotationProcessor.java @@ -26,8 +26,9 @@ import static lombok.core.Augments.ClassLoader_lombokAlreadyAddedTo; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; -import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; @@ -68,7 +69,7 @@ public class AnnotationProcessor extends AbstractProcessor { private final List<String> delayedWarnings = new ArrayList<String>(); /** - * This method is a simplified version of {@link lombok.javac.apt.LombokProcessor.getJavacProcessingEnvironment} + * This method is a simplified version of {@link lombok.javac.apt.LombokProcessor#getJavacProcessingEnvironment} * It simply returns the processing environment, but in case of gradle incremental compilation, * the delegate ProcessingEnvironment of the gradle wrapper is returned. */ @@ -83,15 +84,11 @@ public class AnnotationProcessor extends AbstractProcessor { for (Class<?> procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) { try { - Field field; - try { - field = Permit.getField(procEnvClass, "delegate"); - } catch (NoSuchFieldException e) { - field = Permit.getField(procEnvClass, "processingEnv"); - } - Object delegate = field.get(procEnv); - - return tryRecursivelyObtainJavacProcessingEnvironment((ProcessingEnvironment) delegate); + Object delegate = tryGetDelegateField(procEnvClass, procEnv); + if (delegate == null) delegate = tryGetProcessingEnvField(procEnvClass, procEnv); + if (delegate == null) delegate = tryGetProxyDelegateToField(procEnvClass, procEnv); + + if (delegate != null) return tryRecursivelyObtainJavacProcessingEnvironment((ProcessingEnvironment) delegate); } catch (final Exception e) { // no valid delegate, try superclass } @@ -100,6 +97,40 @@ public class AnnotationProcessor extends AbstractProcessor { return null; } + /** + * Gradle incremental processing + */ + private static Object tryGetDelegateField(Class<?> delegateClass, Object instance) { + try { + return Permit.getField(delegateClass, "delegate").get(instance); + } catch (Exception e) { + return null; + } + } + + /** + * Kotlin incremental processing + */ + private static Object tryGetProcessingEnvField(Class<?> delegateClass, Object instance) { + try { + return Permit.getField(delegateClass, "processingEnv").get(instance); + } catch (Exception e) { + return null; + } + } + + /** + * IntelliJ IDEA >= 2020.3 + */ + private static Object tryGetProxyDelegateToField(Class<?> delegateClass, Object instance) { + try { + InvocationHandler handler = Proxy.getInvocationHandler(instance); + return Permit.getField(handler.getClass(), "val$delegateTo").get(handler); + } catch (Exception e) { + return null; + } + } + static class JavacDescriptor extends ProcessorDescriptor { private Processor processor; @@ -143,7 +174,7 @@ public class AnnotationProcessor extends AbstractProcessor { if (!ClassLoader_lombokAlreadyAddedTo.getAndSet(environmentClassLoader, true)) { Method m = Permit.getMethod(environmentClassLoader.getClass(), "addURL", URL.class); URL selfUrl = new File(ClassRootFinder.findClassRootOfClass(AnnotationProcessor.class)).toURI().toURL(); - m.invoke(environmentClassLoader, selfUrl); + Permit.invoke(m, environmentClassLoader, selfUrl); } } @@ -188,8 +219,12 @@ public class AnnotationProcessor extends AbstractProcessor { if (supported.length() > 0) supported.append(", "); supported.append(proc.getName()); } - procEnv.getMessager().printMessage(Kind.WARNING, String.format("You aren't using a compiler supported by lombok, so lombok will not work and has been disabled.\n" + + if (procEnv.getClass().getName().equals("com.google.turbine.processing.TurbineProcessingEnvironment")) { + procEnv.getMessager().printMessage(Kind.ERROR, String.format("Turbine is not currently supported by lombok.")); + } else { + procEnv.getMessager().printMessage(Kind.WARNING, String.format("You aren't using a compiler supported by lombok, so lombok will not work and has been disabled.\n" + "Your processor is: %s\nLombok supports: %s", procEnv.getClass().getName(), supported)); + } } } @@ -226,6 +261,6 @@ public class AnnotationProcessor extends AbstractProcessor { * We just return the latest version of whatever JDK we run on. Stupid? Yeah, but it's either that or warnings on all versions but 1. Blame Joe. */ @Override public SourceVersion getSupportedSourceVersion() { - return SourceVersion.values()[SourceVersion.values().length - 1]; + return SourceVersion.latest(); } } diff --git a/src/core/lombok/core/Main.java b/src/core/lombok/core/Main.java index 47afaefd..2c9d1dcb 100644 --- a/src/core/lombok/core/Main.java +++ b/src/core/lombok/core/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2018 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; public class Main { private static final Collection<?> HELP_SWITCHES = Collections.unmodifiableList(Arrays.asList( @@ -46,7 +46,7 @@ public class Main { } } - @ProviderFor(LombokApp.class) + @Provides public static class VersionApp extends LombokApp { @Override public String getAppName() { return "version"; @@ -66,7 +66,7 @@ public class Main { } } - @ProviderFor(LombokApp.class) + @Provides public static class LicenseApp extends LombokApp { @Override public String getAppName() { return "license"; diff --git a/src/core/lombok/core/PostCompiler.java b/src/core/lombok/core/PostCompiler.java index 72f4b3a2..122bb268 100644 --- a/src/core/lombok/core/PostCompiler.java +++ b/src/core/lombok/core/PostCompiler.java @@ -28,6 +28,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; public final class PostCompiler { private PostCompiler() {/* prevent instantiation*/}; @@ -67,8 +68,17 @@ public final class PostCompiler { public static OutputStream wrapOutputStream(final OutputStream originalStream, final String fileName, final DiagnosticsReceiver diagnostics) throws IOException { if (System.getProperty("lombok.disablePostCompiler", null) != null) return originalStream; + + // close() can be called more than once and should be idempotent, therefore, ensure we never transform more than once. + final AtomicBoolean closed = new AtomicBoolean(); + return new ByteArrayOutputStream() { @Override public void close() throws IOException { + if (closed.getAndSet(true)) { + originalStream.close(); + return; + } + // no need to call super byte[] original = toByteArray(); byte[] copy = null; diff --git a/src/core/lombok/core/PublicApiCreatorApp.java b/src/core/lombok/core/PublicApiCreatorApp.java index c1430c24..f45808fe 100644 --- a/src/core/lombok/core/PublicApiCreatorApp.java +++ b/src/core/lombok/core/PublicApiCreatorApp.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,10 +36,9 @@ import java.util.zip.ZipEntry; import lombok.Lombok; import lombok.patcher.ClassRootFinder; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(LombokApp.class) +@Provides public class PublicApiCreatorApp extends LombokApp { @Override public String getAppName() { return "publicApi"; diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java index 2962a8e0..af0941bb 100644 --- a/src/core/lombok/core/Version.java +++ b/src/core/lombok/core/Version.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2019 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,7 @@ public class Version { // ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries). // Note: In 'X.Y.Z', if Z is odd, its a snapshot build built from the repository, so many different 0.10.3 versions can exist, for example. // Official builds always end in an even number. (Since 0.10.2). - private static final String VERSION = "1.18.13"; + private static final String VERSION = "1.18.19"; private static final String RELEASE_NAME = "Edgy Guinea Pig"; // private static final String RELEASE_NAME = "Envious Ferret"; diff --git a/src/core/lombok/core/configuration/CheckerFrameworkVersion.java b/src/core/lombok/core/configuration/CheckerFrameworkVersion.java index 5805678f..c37ba91d 100644 --- a/src/core/lombok/core/configuration/CheckerFrameworkVersion.java +++ b/src/core/lombok/core/configuration/CheckerFrameworkVersion.java @@ -32,9 +32,9 @@ public final class CheckerFrameworkVersion implements ConfigurationValueType { public static final String NAME__SIDE_EFFECT_FREE = "org.checkerframework.dataflow.qual.SideEffectFree"; public static final String NAME__PURE = "org.checkerframework.dataflow.qual.Pure"; public static final String NAME__UNIQUE = "org.checkerframework.common.aliasing.qual.Unique"; - public static final String NAME__RETURNS_RECEIVER = "org.checkerframework.checker.builder.qual.ReturnsReceiver"; - public static final String NAME__NOT_CALLED = "org.checkerframework.checker.builder.qual.NotCalledMethods"; - public static final String NAME__CALLED = "org.checkerframework.checker.builder.qual.CalledMethods"; + public static final String NAME__RETURNS_RECEIVER = "org.checkerframework.common.returnsreceiver.qual.This"; + public static final String NAME__NOT_CALLED = "org.checkerframework.checker.calledmethods.qual.NotCalledMethods"; + public static final String NAME__CALLED = "org.checkerframework.checker.calledmethods.qual.CalledMethods"; public static final CheckerFrameworkVersion NONE = new CheckerFrameworkVersion(0); diff --git a/src/core/lombok/core/configuration/ConfigurationApp.java b/src/core/lombok/core/configuration/ConfigurationApp.java index 9c225fdb..8d794c8a 100644 --- a/src/core/lombok/core/configuration/ConfigurationApp.java +++ b/src/core/lombok/core/configuration/ConfigurationApp.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2020 The Project Lombok Authors. + * Copyright (C) 2014-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,8 +41,6 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import org.mangosdk.spi.ProviderFor; - import com.zwitserloot.cmdreader.CmdReader; import com.zwitserloot.cmdreader.Description; import com.zwitserloot.cmdreader.Excludes; @@ -56,8 +54,9 @@ import com.zwitserloot.cmdreader.Shorthand; import lombok.ConfigurationKeys; import lombok.core.LombokApp; import lombok.core.configuration.ConfigurationParser.Collector; +import lombok.spi.Provides; -@ProviderFor(LombokApp.class) +@Provides public class ConfigurationApp extends LombokApp { private static final URI NO_CONFIG = URI.create(""); diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 1c4437d7..19cc5a9e 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; @@ -324,7 +325,9 @@ public class HandlerUtil { "com.fasterxml.jackson.annotation.JsonTypeInfo", "com.fasterxml.jackson.annotation.JsonView", "com.fasterxml.jackson.databind.annotation.JsonDeserialize", + "com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper", "com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty", + "com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText", })); COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] { "com.fasterxml.jackson.annotation.JsonAnySetter", @@ -756,4 +759,90 @@ 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 enum JavadocTag { + PARAM("@param(?:eter)?"), + RETURN("@returns?"); + + private Pattern pattern; + + JavadocTag(String regexpFragment) { + pattern = Pattern.compile("\\s?^[ \\t]*\\**[ \\t]*" + regexpFragment + "(\\S|\\s)*?(?=(\\s^\\s*\\**\\s*@|\\Z))", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + } + } + + public static String stripLinesWithTagFromJavadoc(String javadoc, JavadocTag tag) { + if (javadoc == null || javadoc.isEmpty()) return javadoc; + return tag.pattern.matcher(javadoc).replaceAll(""); + } + + public static String stripSectionsFromJavadoc(String javadoc) { + if (javadoc == null || javadoc.isEmpty()) return 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) { + if (javadoc == null || javadoc.isEmpty()) return null; + 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 (in != null && FIND_RETURN.matcher(in).find()) return in; + + return addJavadocLine(in, "@return {@code this}."); + } + + public static String addReturnsUpdatedSelfIfNeeded(String in) { + if (in != null && 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 == null) return line; + if (in.endsWith("\n")) return in + line + "\n"; + return in + "\n" + line; + } + + public static String getParamJavadoc(String methodComment, String param) { + if (methodComment == null || methodComment.isEmpty()) return methodComment; + Pattern pattern = Pattern.compile("@param " + param + " (\\S|\\s)+?(?=^ ?@)", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(methodComment); + if (matcher.find()) { + return matcher.group(); + } + return null; + } } diff --git a/src/core/lombok/core/handlers/SneakyThrowsAndCleanupDependencyInfo.java b/src/core/lombok/core/handlers/SneakyThrowsAndCleanupDependencyInfo.java index 3b6f8974..482c166b 100644 --- a/src/core/lombok/core/handlers/SneakyThrowsAndCleanupDependencyInfo.java +++ b/src/core/lombok/core/handlers/SneakyThrowsAndCleanupDependencyInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,10 +25,9 @@ import java.util.Arrays; import java.util.List; import lombok.core.runtimeDependencies.RuntimeDependencyInfo; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(RuntimeDependencyInfo.class) +@Provides public class SneakyThrowsAndCleanupDependencyInfo implements RuntimeDependencyInfo { @Override public List<String> getRuntimeDependencies() { return Arrays.asList( diff --git a/src/core/lombok/core/runtimeDependencies/CreateLombokRuntimeApp.java b/src/core/lombok/core/runtimeDependencies/CreateLombokRuntimeApp.java index 4d5988af..21f097d7 100644 --- a/src/core/lombok/core/runtimeDependencies/CreateLombokRuntimeApp.java +++ b/src/core/lombok/core/runtimeDependencies/CreateLombokRuntimeApp.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,8 +38,7 @@ import java.util.zip.ZipEntry; import lombok.core.LombokApp; import lombok.core.SpiLoadUtil; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.zwitserloot.cmdreader.CmdReader; import com.zwitserloot.cmdreader.Description; @@ -48,7 +47,7 @@ import com.zwitserloot.cmdreader.Mandatory; import com.zwitserloot.cmdreader.Requires; import com.zwitserloot.cmdreader.Shorthand; -@ProviderFor(LombokApp.class) +@Provides public class CreateLombokRuntimeApp extends LombokApp { private List<RuntimeDependencyInfo> infoObjects; diff --git a/src/core/lombok/eclipse/EclipseAugments.java b/src/core/lombok/eclipse/EcjAugments.java index f4583ac4..cd7173a8 100644 --- a/src/core/lombok/eclipse/EclipseAugments.java +++ b/src/core/lombok/eclipse/EcjAugments.java @@ -21,14 +21,21 @@ */ 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.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.core.CompilationUnit; +import org.eclipse.jdt.internal.core.SourceMethod; import lombok.core.FieldAugment; -public final class EclipseAugments { - private EclipseAugments() { +public final class EcjAugments { + private EcjAugments() { // Prevent instantiation } @@ -36,4 +43,13 @@ 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<ICompilationUnit, Map<String, String>> CompilationUnit_javadoc = FieldAugment.augment(ICompilationUnit.class, Map.class, "$javadoc"); + + public static final class EclipseAugments { + private EclipseAugments() { + // Prevent instantiation + } + + 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/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java index 2c860e14..0036ec23 100644 --- a/src/core/lombok/eclipse/EclipseAST.java +++ b/src/core/lombok/eclipse/EclipseAST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2018 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,11 +21,8 @@ */ package lombok.eclipse; -import static lombok.eclipse.handlers.EclipseHandlerUtil.*; - import java.io.File; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.util.ArrayList; @@ -33,9 +30,9 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import lombok.Lombok; import lombok.core.AST; import lombok.core.LombokImmutableList; +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import lombok.permit.Permit; import org.eclipse.core.resources.ResourcesPlugin; @@ -287,23 +284,8 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { */ public static void addProblemToCompilationResult(char[] fileNameArray, CompilationResult result, boolean isWarning, String message, int sourceStart, int sourceEnd) { - try { - EcjReflectionCheck.addProblemToCompilationResult.invoke(null, fileNameArray, result, isWarning, message, sourceStart, sourceEnd); - } catch (NoClassDefFoundError e) { - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - } catch (IllegalAccessException e) { - throw Lombok.sneakyThrow(e); - } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e); - } catch (NullPointerException e) { - if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { - e.initCause(EcjReflectionCheck.problemAddProblemToCompilationResult); - throw e; - } - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - } + + Permit.invokeSneaky(EcjReflectionCheck.problemAddProblemToCompilationResult, EcjReflectionCheck.addProblemToCompilationResult, null, fileNameArray, result, isWarning, message, sourceStart, sourceEnd); } public static Annotation[] getTopLevelTypeReferenceAnnotations(TypeReference tr) { @@ -311,14 +293,14 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { if (m == null) return null; Annotation[][] annss = null; try { - annss = (Annotation[][]) m.invoke(tr); + annss = (Annotation[][]) Permit.invoke(m, tr); if (annss != null) return annss[0]; } catch (Throwable ignore) {} try { Field f = EcjReflectionCheck.typeReferenceAnnotations; if (f == null) return null; - annss = (Annotation[][]) f.get(tr); + annss = (Annotation[][]) Permit.get(f, tr); if (annss == null) return null; return annss[annss.length - 1]; } catch (Throwable t) { @@ -411,7 +393,9 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { private Collection<EclipseNode> buildFields(FieldDeclaration[] children, Annotation[][] annotations) { List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); - if (children != null) for (int i = 0; i < children.length; i++) addIfNotNull(childNodes, buildField(children[i], annotations[i])); + if (children != null) for (int i = 0; i < children.length; i++) { + addIfNotNull(childNodes, buildField(children[i], annotations[i])); + } return childNodes; } @@ -422,7 +406,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { } private EclipseNode buildField(FieldDeclaration field, Annotation[] annotations) { - if (field instanceof Initializer) return buildInitializer((Initializer)field); + if (field instanceof Initializer) return buildInitializer((Initializer) field); if (setAndGetAsHandled(field)) return null; List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); addIfNotNull(childNodes, buildTypeUse(field.type)); diff --git a/src/core/lombok/eclipse/EclipseNode.java b/src/core/lombok/eclipse/EclipseNode.java index 361a8d42..4f86f0a5 100644 --- a/src/core/lombok/eclipse/EclipseNode.java +++ b/src/core/lombok/eclipse/EclipseNode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/core/lombok/eclipse/HandlerLibrary.java b/src/core/lombok/eclipse/HandlerLibrary.java index 0e72fb38..8d69325e 100644 --- a/src/core/lombok/eclipse/HandlerLibrary.java +++ b/src/core/lombok/eclipse/HandlerLibrary.java @@ -23,7 +23,7 @@ package lombok.eclipse; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; -import static lombok.eclipse.EclipseAugments.ASTNode_handled; +import static lombok.eclipse.EcjAugments.ASTNode_handled; import java.io.IOException; import java.lang.annotation.Annotation; diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index c70e4e5c..defe8d34 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.Eclipse.*; -import static lombok.eclipse.EclipseAugments.*; +import static lombok.eclipse.EcjAugments.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.EclipseReflectiveMembers.*; import java.lang.reflect.Array; @@ -39,6 +39,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +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 +95,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; @@ -121,10 +123,10 @@ import lombok.core.configuration.TypeName; import lombok.core.debug.ProblemReporter; import lombok.core.handlers.HandlerUtil; import lombok.core.handlers.HandlerUtil.FieldAccess; +import lombok.eclipse.EcjAugments; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAST; import lombok.eclipse.EclipseNode; -import lombok.eclipse.Java14Bits; import lombok.experimental.Accessors; import lombok.experimental.Tolerate; import lombok.permit.Permit; @@ -289,9 +291,7 @@ public class EclipseHandlerUtil { MarkerAnnotation ann = new MarkerAnnotation(copyType(annotation.type, source), pS); setGeneratedBy(ann, source); ann.declarationSourceEnd = ann.sourceEnd = ann.statementEnd = pE; - try { - reflectSet(ANNOTATION__MEMBER_VALUE_PAIR_NAME, ann, reflect(ANNOTATION__MEMBER_VALUE_PAIR_NAME, annotation)); - } catch (Exception ignore) { /* Various eclipse versions don't have it */ } + copyMemberValuePairName(ann, annotation); return ann; } @@ -300,9 +300,7 @@ public class EclipseHandlerUtil { setGeneratedBy(ann, source); ann.declarationSourceEnd = ann.sourceEnd = ann.statementEnd = pE; ann.memberValue = copyAnnotationMemberValue(((SingleMemberAnnotation) annotation).memberValue); - try { - reflectSet(ANNOTATION__MEMBER_VALUE_PAIR_NAME, ann, reflect(ANNOTATION__MEMBER_VALUE_PAIR_NAME, annotation)); - } catch (Exception ignore) { /* Various eclipse versions don't have it */ } + copyMemberValuePairName(ann, annotation); return ann; } @@ -318,15 +316,21 @@ public class EclipseHandlerUtil { for (int i = 0; i < inPairs.length; i++) ann.memberValuePairs[i] = new MemberValuePair(inPairs[i].name, inPairs[i].sourceStart, inPairs[i].sourceEnd, copyAnnotationMemberValue(inPairs[i].value)); } - try { - reflectSet(ANNOTATION__MEMBER_VALUE_PAIR_NAME, ann, reflect(ANNOTATION__MEMBER_VALUE_PAIR_NAME, annotation)); - } catch (Exception ignore) { /* Various eclipse versions don't have it */ } + copyMemberValuePairName(ann, annotation); return ann; } return annotation; } + private static void copyMemberValuePairName(Annotation source, Annotation target) { + if (ANNOTATION__MEMBER_VALUE_PAIR_NAME == null) return; + + try { + reflectSet(ANNOTATION__MEMBER_VALUE_PAIR_NAME, source, reflect(ANNOTATION__MEMBER_VALUE_PAIR_NAME, target)); + } catch (Exception ignore) { /* Various eclipse versions don't have it */ } + } + static class EclipseReflectiveMembers { public static final Field STRING_LITERAL__LINE_NUMBER; public static final Field ANNOTATION__MEMBER_VALUE_PAIR_NAME; @@ -372,7 +376,7 @@ public class EclipseHandlerUtil { private static Class<?> getClass(String fqn) { try { return Class.forName(fqn); - } catch (Exception e) { + } catch (Throwable t) { return null; } } @@ -380,7 +384,7 @@ public class EclipseHandlerUtil { private static Field getField(Class<?> c, String fName) { try { return Permit.getField(c, fName); - } catch (Exception e) { + } catch (Throwable t) { return null; } } @@ -775,10 +779,9 @@ public class EclipseHandlerUtil { public static boolean hasNonNullAnnotations(EclipseNode node) { for (EclipseNode child : node.down()) { - if (child.getKind() == Kind.ANNOTATION) { - Annotation annotation = (Annotation) child.get(); - for (String bn : NONNULL_ANNOTATIONS) if (typeMatches(bn, node, annotation.type)) return true; - } + if (child.getKind() != Kind.ANNOTATION) continue; + Annotation annotation = (Annotation) child.get(); + for (String bn : NONNULL_ANNOTATIONS) if (typeMatches(bn, node, annotation.type)) return true; } return false; } @@ -1873,15 +1876,12 @@ public class EclipseHandlerUtil { public static MemberExistsResult constructorExists(EclipseNode node) { node = upToTypeNode(node); if (node != null && node.get() instanceof TypeDeclaration) { - TypeDeclaration typeDecl = (TypeDeclaration)node.get(); + TypeDeclaration typeDecl = (TypeDeclaration) node.get(); if (typeDecl.methods != null) for (AbstractMethodDeclaration def : typeDecl.methods) { - if (def instanceof ConstructorDeclaration) { - if ((def.bits & ASTNode.IsDefaultConstructor) != 0) continue; - - if (isTolerate(node, def)) continue; - - return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; - } + if (!(def instanceof ConstructorDeclaration)) continue; + if ((def.bits & ASTNode.IsDefaultConstructor) != 0) continue; + if (isTolerate(node, def)) continue; + return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; } } @@ -1889,24 +1889,19 @@ public class EclipseHandlerUtil { } /** - * Checks if there is a constructor generated by lombok. + * Checks if there is at least one constructor that is generated by lombok. * * @param node Any node that represents the Type (TypeDeclaration) to look in, or any child node thereof. */ public static boolean lombokConstructorExists(EclipseNode node) { node = upToTypeNode(node); if (node != null && node.get() instanceof TypeDeclaration) { - TypeDeclaration typeDecl = (TypeDeclaration)node.get(); + TypeDeclaration typeDecl = (TypeDeclaration) node.get(); if (typeDecl.methods != null) for (AbstractMethodDeclaration def : typeDecl.methods) { - if (def instanceof ConstructorDeclaration) { - if ((def.bits & (ASTNode.IsDefaultConstructor | Java14Bits.IsCanonicalConstructor)) != 0) continue; - - if (isTolerate(node, def)) continue; - - if (getGeneratedBy(def) != null) { - return true; - } - } + if (!(def instanceof ConstructorDeclaration)) continue; + if ((def.bits & ASTNode.IsDefaultConstructor | IsCanonicalConstructor) != 0) continue; + if (isTolerate(node, def)) continue; + if (getGeneratedBy(def) != null) return true; } } @@ -2331,18 +2326,10 @@ public class EclipseHandlerUtil { public static IntLiteral makeIntLiteral(char[] token, ASTNode source) { int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd; IntLiteral result; - try { - if (intLiteralConstructor != null) { - result = intLiteralConstructor.newInstance(token, pS, pE); - } else { - result = (IntLiteral) intLiteralFactoryMethod.invoke(null, token, pS, pE); - } - } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e.getCause()); - } catch (IllegalAccessException e) { - throw Lombok.sneakyThrow(e); - } catch (InstantiationException e) { - throw Lombok.sneakyThrow(e); + if (intLiteralConstructor != null) { + result = Permit.newInstanceSneaky(intLiteralConstructor, token, pS, pE); + } else { + result = (IntLiteral) Permit.invokeSneaky(intLiteralFactoryMethod, null, token, pS, pE); } if (source != null) setGeneratedBy(result, source); @@ -2496,18 +2483,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) { @@ -2662,39 +2638,57 @@ public class EclipseHandlerUtil { return ref; } - static boolean isClass(EclipseNode typeNode) { - return isClassAndDoesNotHaveFlags(typeNode, ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation | Java14Bits.AccRecord); + public static TypeReference createTypeReference(String typeName, ASTNode source) { + return generateQualifiedTypeRef(source, fromQualifiedName(typeName)); } - static boolean isClassOrEnum(EclipseNode typeNode) { - return isClassAndDoesNotHaveFlags(typeNode, ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | Java14Bits.AccRecord); + /** + * Returns {@code true} if the provided node is an actual class and not some other type declaration (so, not an annotation definition, interface, enum, or record). + */ + public static boolean isClass(EclipseNode typeNode) { + return isTypeAndDoesNotHaveFlags(typeNode, ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation | AccRecord); } - public static boolean isClassAndDoesNotHaveFlags(EclipseNode typeNode, long flags) { + /** + * Returns {@code true} if the provided node is an actual class or enum and not some other type declaration (so, not an annotation definition, interface, or record). + */ + public static boolean isClassOrEnum(EclipseNode typeNode) { + return isTypeAndDoesNotHaveFlags(typeNode, ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | AccRecord); + } + + /** + * Returns {@code true} if the provided node is a record declaration (so, not an annotation definition, interface, enum, or plain class). + */ + public static boolean isRecord(EclipseNode typeNode) { TypeDeclaration typeDecl = null; if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - return (modifiers & flags) == 0; + return (modifiers & AccRecord) != 0; } - public static boolean isRecord(EclipseNode typeNode) { - return typeNode.getKind() == Kind.TYPE && isRecord((TypeDeclaration) typeNode.get()); + /** + * Returns {@code true} If the provided node is a field declaration, and represents a field in a {@code record} declaration. + */ + public static boolean isRecordField(EclipseNode fieldNode) { + return fieldNode.getKind() == Kind.FIELD && (((FieldDeclaration) fieldNode.get()).modifiers & AccRecord) != 0; } - public static boolean isRecord(TypeDeclaration typeDeclaration) { - return (typeDeclaration.modifiers & Java14Bits.AccRecord) != 0; + /** + * Returns {@code true) if the provided node is a type declaration <em>and</em> is <strong>not</strong> of any kind indicated by the flags (the intent is to pass flags usch as `ClassFileConstants.AccEnum`). + */ + static boolean isTypeAndDoesNotHaveFlags(EclipseNode typeNode, long flags) { + TypeDeclaration typeDecl = null; + if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); + int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; + return (modifiers & flags) == 0; } - public static boolean isRecordField(EclipseNode fieldNode) { - return fieldNode.getKind() == Kind.FIELD && (((FieldDeclaration) fieldNode.get()).modifiers & Java14Bits.AccRecord) != 0; - } - public static AbstractVariableDeclaration[] getRecordComponents(TypeDeclaration typeDeclaration) { - if (!isRecord(typeDeclaration)) return null; + if (typeDeclaration == null || (typeDeclaration.modifiers & AccRecord) == 0) return null; try { return (AbstractVariableDeclaration[]) TYPE_DECLARATION_RECORD_COMPONENTS.get(typeDeclaration); } catch (Exception e) { - // Ignore + // This presumably means this isn't a JDK16 - fall through. } return null; } @@ -2707,11 +2701,154 @@ public class EclipseHandlerUtil { if (recordComponents != null) { int j = 0; for (int i = 0; i < typeDeclaration.fields.length; i++) { - if ((typeDeclaration.fields[i].modifiers & Java14Bits.AccRecord) != 0) { + if ((typeDeclaration.fields[i].modifiers & AccRecord) != 0) { annotations[i] = recordComponents[j++].annotations; } } } return annotations; } + + 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 (doc == null) return; + + Map<String, String> docs = EcjAugments.CompilationUnit_javadoc.setIfAbsent(cud.compilationResult.compilationUnit, new HashMap<String, String>()); + if (node instanceof AbstractMethodDeclaration) { + AbstractMethodDeclaration methodDeclaration = (AbstractMethodDeclaration) node; + String signature = getSignature(type, methodDeclaration); + /* Add javadoc start marker, remove trailing line break, add leading asterisks to each line, add javadoc end marker */ + docs.put(signature, String.format("/**%n%s%n */", doc.replaceAll("$\\r?\\n", "").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) { + sb.append(String.valueOf(argument.type)); + } + } + 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), JavadocTag.PARAM); + } + 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), JavadocTag.RETURN); + } + 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 (forceAddReturn) { + newJavadoc = addReturnsThisIfNeeded(newJavadoc); + } + setDocComment(cud, type, to, newJavadoc); + } catch (Exception ignore) {} + } } diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index 85243ec1..d099cab2 100755 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -68,6 +68,7 @@ import lombok.core.SpiLoadUtil; import lombok.core.TypeLibrary; import lombok.core.configuration.CheckerFrameworkVersion; import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.HandleBuilder.BuilderJob; public class EclipseSingularsRecipes { public interface TypeReferenceMaker { @@ -258,20 +259,20 @@ public class EclipseSingularsRecipes { * If you need more control over the return type and value, use * {@link #generateMethods(SingularData, boolean, EclipseNode, boolean, TypeReferenceMaker, StatementMaker)}. */ - public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, final EclipseNode builderType, boolean fluent, final boolean chain, AccessLevel access) { + public void generateMethods(final BuilderJob job, SingularData data, boolean deprecate) { TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() { @Override public TypeReference make() { - return chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); + return job.oldChain ? cloneSelfType(job.builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); } }; StatementMaker returnStatementMaker = new StatementMaker() { @Override public ReturnStatement make() { - return chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; + return job.oldChain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; } }; - generateMethods(cfv, data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker, access); + generateMethods(job.checkerFramework, data, deprecate, job.builderType, job.oldFluent, returnTypeMaker, returnStatementMaker, job.accessInners); } /** @@ -450,7 +451,17 @@ public class EclipseSingularsRecipes { statements.add(0, nullCheck); } + protected abstract int getTypeArgumentsCount(); + protected abstract char[][] getEmptyMakerReceiver(String targetFqn); protected abstract char[] getEmptyMakerSelector(String targetFqn); + + public MessageSend getEmptyExpression(String targetFqn, SingularData data, EclipseNode typeNode, ASTNode source) { + MessageSend send = new MessageSend(); + send.receiver = generateQualifiedNameRef(source, getEmptyMakerReceiver(targetFqn)); + send.selector = getEmptyMakerSelector(targetFqn); + send.typeArguments = createTypeArgs(getTypeArgumentsCount(), false, typeNode, data.getTypeArgs()); + return send; + } } } diff --git a/src/core/lombok/eclipse/handlers/HandleAccessors.java b/src/core/lombok/eclipse/handlers/HandleAccessors.java index 864ff50b..6a92dee2 100644 --- a/src/core/lombok/eclipse/handlers/HandleAccessors.java +++ b/src/core/lombok/eclipse/handlers/HandleAccessors.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Project Lombok Authors. + * Copyright (C) 2014-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,11 +28,11 @@ import lombok.core.HandlerPriority; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.Accessors; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.mangosdk.spi.ProviderFor; -@ProviderFor(EclipseAnnotationHandler.class) +@Provides @HandlerPriority(65536) public class HandleAccessors extends EclipseAnnotationHandler<Accessors> { @Override public void handle(AnnotationValues<Accessors> annotation, Annotation ast, EclipseNode annotationNode) { diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 5054703a..c2b73988 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2020 The Project Lombok Authors. + * Copyright (C) 2013-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,11 +21,10 @@ */ 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; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -75,7 +74,6 @@ import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; -import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; import lombok.Builder; @@ -84,27 +82,38 @@ 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; import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists; import lombok.experimental.NonFinal; +import lombok.spi.Provides; -@ProviderFor(EclipseAnnotationHandler.class) +@Provides @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes. public class HandleBuilder extends EclipseAnnotationHandler<Builder> { private HandleConstructor handleConstructor = new HandleConstructor(); - private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray(); - private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray(); + static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray(); + static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray(); + static final String TO_BUILDER_METHOD_NAME_STRING = "toBuilder"; + static final char[] TO_BUILDER_METHOD_NAME = TO_BUILDER_METHOD_NAME_STRING.toCharArray(); + static final char[] DEFAULT_PREFIX = {'$', 'd', 'e', 'f', 'a', 'u', 'l', 't', '$'}; + static final char[] SET_PREFIX = {'$', 's', 'e', 't'}; + static final char[] VALUE_PREFIX = {'$', 'v', 'a', 'l', 'u', 'e'}; + static final char[] BUILDER_TEMP_VAR = {'b', 'u', 'i', 'l', 'd', 'e', 'r'}; + static final AbstractMethodDeclaration[] EMPTY_METHODS = {}; + static final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type."; private static final boolean toBoolean(Object expr, boolean defaultValue) { if (expr == null) return defaultValue; @@ -113,6 +122,95 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return ((Boolean) expr).booleanValue(); } + static class BuilderJob { + CheckerFrameworkVersion checkerFramework; + EclipseNode parentType; + String builderMethodName, buildMethodName; + boolean isStatic; + TypeParameter[] typeParams; + TypeParameter[] builderTypeParams; + ASTNode source; + EclipseNode sourceNode; + List<BuilderFieldData> builderFields; + AccessLevel accessInners, accessOuters; + boolean oldFluent, oldChain, toBuilder; + + EclipseNode builderType; + String builderClassName; + char[] builderClassNameArr; + + void setBuilderClassName(String builderClassName) { + this.builderClassName = builderClassName; + this.builderClassNameArr = builderClassName.toCharArray(); + } + + TypeParameter[] copyTypeParams() { + return EclipseHandlerUtil.copyTypeParams(typeParams, source); + } + + long getPos() { + return ((long) source.sourceStart) << 32 | source.sourceEnd; + } + + public TypeReference createBuilderTypeReference() { + return namePlusTypeParamsToTypeReference(parentType, builderClassNameArr, !isStatic, builderTypeParams, getPos()); + } + + public TypeReference createBuilderTypeReferenceForceStatic() { + return namePlusTypeParamsToTypeReference(parentType, builderClassNameArr, false, builderTypeParams, getPos()); + } + + public TypeReference createBuilderParentTypeReference() { + return namePlusTypeParamsToTypeReference(parentType, typeParams, getPos()); + } + + public EclipseNode getTopNode() { + return parentType.top(); + } + + void init(AnnotationValues<Builder> annValues, Builder ann, EclipseNode node) { + accessOuters = ann.access(); + if (accessOuters == null) accessOuters = AccessLevel.PUBLIC; + if (accessOuters == AccessLevel.NONE) { + sourceNode.addError("AccessLevel.NONE is not valid here"); + accessOuters = AccessLevel.PUBLIC; + } + accessInners = accessOuters == AccessLevel.PROTECTED ? AccessLevel.PUBLIC : accessOuters; + + // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them. + oldFluent = toBoolean(annValues.getActualExpression("fluent"), true); + oldChain = toBoolean(annValues.getActualExpression("chain"), true); + + builderMethodName = ann.builderMethodName(); + buildMethodName = ann.buildMethodName(); + setBuilderClassName(fixBuilderClassName(node, ann.builderClassName())); + toBuilder = ann.toBuilder(); + + if (builderMethodName == null) builderMethodName = "builder"; + if (buildMethodName == null) buildMethodName = "build"; + } + + static String fixBuilderClassName(EclipseNode node, String override) { + if (override != null && !override.isEmpty()) return override; + override = node.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME); + if (override != null && !override.isEmpty()) return override; + return "*Builder"; + } + + MethodDeclaration createNewMethodDeclaration() { + return new MethodDeclaration(((CompilationUnitDeclaration) getTopNode().get()).compilationResult); + } + + String replaceBuilderClassName(char[] name) { + if (builderClassName.indexOf('*') == -1) return builderClassName; + return builderClassName.replace("*", new String(name)); + } + + String replaceBuilderClassName(String name) { + return builderClassName.replace("*", name); + } + } + static class BuilderFieldData { Annotation[] annotations; TypeReference type; @@ -147,10 +245,6 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return true; } - private static final char[] DEFAULT_PREFIX = {'$', 'd', 'e', 'f', 'a', 'u', 'l', 't', '$'}; - private static final char[] SET_PREFIX = {'$', 's', 'e', 't'}; - private static final char[] VALUE_PREFIX = {'$', 'v', 'a', 'l', 'u', 'e'}; - private static final char[] prefixWith(char[] prefix, char[] name) { char[] out = new char[prefix.length + name.length]; System.arraycopy(prefix, 0, out, 0, prefix.length); @@ -159,88 +253,65 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } @Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) { - handleFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); - CheckerFrameworkVersion cfv = getCheckerFrameworkVersion(annotationNode); - - long p = (long) ast.sourceStart << 32 | ast.sourceEnd; + final String BUILDER_NODE_NOT_SUPPORTED_ERR = "@Builder is only supported on classes, records, constructors, and methods."; - Builder builderInstance = annotation.getInstance(); - AccessLevel accessForOuters = builderInstance.access(); - if (accessForOuters == null) accessForOuters = AccessLevel.PUBLIC; - if (builderInstance.access() == AccessLevel.NONE) { - annotationNode.addError("AccessLevel.NONE is not valid here"); - accessForOuters = AccessLevel.PUBLIC; - } - AccessLevel accessForInners = accessForOuters == AccessLevel.PROTECTED ? AccessLevel.PUBLIC : accessForOuters; - - // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them. - boolean fluent = toBoolean(annotation.getActualExpression("fluent"), true); - boolean chain = toBoolean(annotation.getActualExpression("chain"), true); + handleFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); + BuilderJob job = new BuilderJob(); + job.sourceNode = annotationNode; + job.source = ast; + job.checkerFramework = getCheckerFrameworkVersion(annotationNode); + job.isStatic = true; - String builderMethodName = builderInstance.builderMethodName(); - String buildMethodName = builderInstance.buildMethodName(); - String builderClassName = builderInstance.builderClassName(); - String toBuilderMethodName = "toBuilder"; - boolean toBuilder = builderInstance.toBuilder(); + Builder annInstance = annotation.getInstance(); + job.init(annotation, annInstance, annotationNode); List<char[]> typeArgsForToBuilder = null; - if (builderMethodName == null) builderMethodName = "builder"; - if (buildMethodName == null) buildMethodName = "build"; - if (builderClassName == null) builderClassName = ""; - boolean generateBuilderMethod; - if (builderMethodName.isEmpty()) { + if (job.builderMethodName.isEmpty()) { generateBuilderMethod = false; - } else if (!checkName("builderMethodName", builderMethodName, annotationNode)) { + } else if (!checkName("builderMethodName", job.builderMethodName, annotationNode)) { return; } else { generateBuilderMethod = true; } - if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; - if (!builderClassName.isEmpty()) { - if (!checkName("builderClassName", builderClassName, annotationNode)) return; - } + if (!checkName("buildMethodName", job.buildMethodName, annotationNode)) return; EclipseNode parent = annotationNode.up(); - List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>(); - TypeReference returnType; - TypeParameter[] typeParams; - TypeReference[] thrownExceptions; - char[] nameOfStaticBuilderMethod; - EclipseNode tdParent; + job.builderFields = new ArrayList<BuilderFieldData>(); + TypeReference buildMethodReturnType; + TypeReference[] buildMethodThrownExceptions; + char[] nameOfBuilderMethod; EclipseNode fillParametersFrom = parent.get() instanceof AbstractMethodDeclaration ? parent : null; boolean addCleaning = false; - boolean isStatic = true; List<EclipseNode> nonFinalNonDefaultedFields = null; - if (builderClassName.isEmpty()) builderClassName = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME); - if (builderClassName == null || builderClassName.isEmpty()) builderClassName = "*Builder"; - boolean replaceNameInBuilderClassName = builderClassName.contains("*"); - if (parent.get() instanceof TypeDeclaration) { - tdParent = parent; - TypeDeclaration td = (TypeDeclaration) tdParent.get(); + if (!isClass(parent) && !isRecord(parent)) { + annotationNode.addError(BUILDER_NODE_NOT_SUPPORTED_ERR); + return; + } + + job.parentType = parent; + TypeDeclaration td = (TypeDeclaration) parent.get(); List<EclipseNode> allFields = new ArrayList<EclipseNode>(); boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); - for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { + for (EclipseNode fieldNode : HandleConstructor.findAllFields(parent, true)) { FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode); boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); - Annotation[] copyableAnnotations = findCopyableAnnotations(fieldNode); - BuilderFieldData bfd = new BuilderFieldData(); bfd.rawName = fieldNode.getName().toCharArray(); bfd.name = removePrefixFromField(fieldNode); bfd.builderFieldName = bfd.name; - bfd.annotations = copyAnnotations(fd, copyableAnnotations); + bfd.annotations = copyAnnotations(fd, findCopyableAnnotations(fieldNode)); bfd.type = fd.type; - bfd.singularData = getSingularData(fieldNode, ast, builderInstance.setterPrefix()); + bfd.singularData = getSingularData(fieldNode, ast, annInstance.setterPrefix()); bfd.originalFieldNode = fieldNode; if (bfd.singularData != null && isDefault != null) { @@ -265,24 +336,26 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { bfd.builderFieldName = prefixWith(bfd.name, VALUE_PREFIX); MethodDeclaration md = generateDefaultProvider(bfd.nameOfDefaultProvider, td.typeParameters, fieldNode, ast); - if (md != null) injectMethod(tdParent, md); + if (md != null) injectMethod(parent, md); } addObtainVia(bfd, fieldNode); - builderFields.add(bfd); + job.builderFields.add(bfd); allFields.add(fieldNode); } - if (!isRecord(tdParent)) { - handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, + if (!isRecord(parent)) { + // Records ship with a canonical constructor that acts as @AllArgsConstructor - just use that one. + + handleConstructor.generateConstructor(parent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, Collections.<Annotation>emptyList(), annotationNode); } - returnType = namePlusTypeParamsToTypeReference(tdParent, td.typeParameters, p); - typeParams = td.typeParameters; - thrownExceptions = null; - nameOfStaticBuilderMethod = null; - if (replaceNameInBuilderClassName) builderClassName = builderClassName.replace("*", new String(td.name)); - replaceNameInBuilderClassName = false; + job.typeParams = job.builderTypeParams = td.typeParameters; + buildMethodReturnType = job.createBuilderParentTypeReference(); + buildMethodThrownExceptions = null; + nameOfBuilderMethod = null; + job.setBuilderClassName(job.replaceBuilderClassName(td.name)); + if (!checkName("builderClassName", job.builderClassName, annotationNode)) return; } else if (parent.get() instanceof ConstructorDeclaration) { ConstructorDeclaration cd = (ConstructorDeclaration) parent.get(); if (cd.typeParameters != null && cd.typeParameters.length > 0) { @@ -290,21 +363,20 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return; } - tdParent = parent.up(); - TypeDeclaration td = (TypeDeclaration) tdParent.get(); - returnType = namePlusTypeParamsToTypeReference(tdParent, td.typeParameters, p); - typeParams = td.typeParameters; - thrownExceptions = cd.thrownExceptions; - nameOfStaticBuilderMethod = null; - if (replaceNameInBuilderClassName) builderClassName = builderClassName.replace("*", new String(cd.selector)); - replaceNameInBuilderClassName = false; + job.parentType = parent.up(); + TypeDeclaration td = (TypeDeclaration) job.parentType.get(); + job.typeParams = job.builderTypeParams = td.typeParameters; + buildMethodReturnType = job.createBuilderParentTypeReference(); + buildMethodThrownExceptions = cd.thrownExceptions; + nameOfBuilderMethod = null; + job.setBuilderClassName(job.replaceBuilderClassName(cd.selector)); + if (!checkName("builderClassName", job.builderClassName, annotationNode)) return; } else if (parent.get() instanceof MethodDeclaration) { MethodDeclaration md = (MethodDeclaration) parent.get(); - tdParent = parent.up(); - isStatic = md.isStatic(); + job.parentType = parent.up(); + job.isStatic = md.isStatic(); - if (toBuilder) { - final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type."; + if (job.toBuilder) { char[] token; char[][] pkg = null; if (md.returnType.dimensions() > 0) { @@ -330,12 +402,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return; } - if (tdParent == null || !equals(tdParent.getName(), token)) { + if (job.parentType == null || !equals(job.parentType.getName(), token)) { annotationNode.addError(TO_BUILDER_NOT_SUPPORTED); return; } - TypeParameter[] tpOnType = ((TypeDeclaration) tdParent.get()).typeParameters; + TypeParameter[] tpOnType = ((TypeDeclaration) job.parentType.get()).typeParameters; TypeParameter[] tpOnMethod = md.typeParameters; TypeReference[][] tpOnRet_ = null; if (md.returnType instanceof ParameterizedSingleTypeReference) { @@ -374,18 +446,18 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } } - returnType = copyType(md.returnType, ast); - typeParams = md.typeParameters; - thrownExceptions = md.thrownExceptions; - nameOfStaticBuilderMethod = md.selector; - if (replaceNameInBuilderClassName) { - char[] token = returnTypeToBuilderClassName(annotationNode, md, typeParams); - if (token == null) - return; - builderClassName = builderClassName.replace("*", new String(token)); + job.typeParams = job.builderTypeParams = md.typeParameters; + buildMethodReturnType = copyType(md.returnType, ast); + buildMethodThrownExceptions = md.thrownExceptions; + nameOfBuilderMethod = md.selector; + if (job.builderClassName.indexOf('*') > -1) { + char[] token = returnTypeToBuilderClassName(annotationNode, md, job.typeParams); + if (token == null) return; // should not happen. + job.setBuilderClassName(job.replaceBuilderClassName(token)); + if (!checkName("builderClassName", job.builderClassName, annotationNode)) return; } } else { - annotationNode.addError("@Builder is only supported on types, constructors, and methods."); + annotationNode.addError(BUILDER_NODE_NOT_SUPPORTED_ERR); return; } @@ -402,40 +474,39 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { bfd.builderFieldName = bfd.name; bfd.annotations = copyAnnotations(arg, copyableAnnotations); bfd.type = arg.type; - bfd.singularData = getSingularData(param, ast, builderInstance.setterPrefix()); + bfd.singularData = getSingularData(param, ast, annInstance.setterPrefix()); bfd.originalFieldNode = param; addObtainVia(bfd, param); - builderFields.add(bfd); + job.builderFields.add(bfd); } } - EclipseNode builderType = findInnerClass(tdParent, builderClassName); - if (builderType == null) { - builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast, accessForOuters); - } else { - TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get(); - if (isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) { + job.builderType = findInnerClass(job.parentType, job.builderClassName); + if (job.builderType == null) makeBuilderClass(job); + else { + TypeDeclaration builderTypeDeclaration = (TypeDeclaration) job.builderType.get(); + if (job.isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) { annotationNode.addError("Existing Builder must be a static inner class."); return; - } else if (!isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) != 0) { + } else if (!job.isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) != 0) { annotationNode.addError("Existing Builder must be a non-static inner class."); return; } - sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); + sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(job.builderType, annotationNode); /* generate errors for @Singular BFDs that have one already defined node. */ { - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { SingularData sd = bfd.singularData; if (sd == null) continue; EclipseSingularizer singularizer = sd.getSingularizer(); if (singularizer == null) continue; - if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) { + if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderType, sd)) { bfd.singularData = null; } } } } - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { if (bfd.singularData.getSingularizer().requiresCleaning()) { addCleaning = true; @@ -454,64 +525,64 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } } - generateBuilderFields(builderType, builderFields, ast); + generateBuilderFields(job); if (addCleaning) { FieldDeclaration cleanDecl = new FieldDeclaration(CLEAN_FIELD_NAME, 0, -1); cleanDecl.declarationSourceEnd = -1; cleanDecl.modifiers = ClassFileConstants.AccPrivate; cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); cleanDecl.traverse(new SetGeneratedByVisitor(ast), (MethodScope) null); - injectFieldAndMarkGenerated(builderType, cleanDecl); + injectFieldAndMarkGenerated(job.builderType, cleanDecl); } - if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { + if (constructorExists(job.builderType) == MemberExistsResult.NOT_EXISTS) { ConstructorDeclaration cd = HandleConstructor.createConstructor( - AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false, + AccessLevel.PACKAGE, job.builderType, Collections.<EclipseNode>emptyList(), false, annotationNode, Collections.<Annotation>emptyList()); - if (cd != null) injectMethod(builderType, cd); + if (cd != null) injectMethod(job.builderType, cd); } - for (BuilderFieldData bfd : builderFields) { - makePrefixedSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, fluent, chain, accessForInners, bfd.originalFieldNode, builderInstance.setterPrefix()); + for (BuilderFieldData bfd : job.builderFields) { + makePrefixedSetterMethodsForBuilder(job, bfd, annInstance.setterPrefix()); } { - MemberExistsResult methodExists = methodExists(buildMethodName, builderType, -1); - if (methodExists == MemberExistsResult.EXISTS_BY_LOMBOK) methodExists = methodExists(buildMethodName, builderType, 0); + MemberExistsResult methodExists = methodExists(job.buildMethodName, job.builderType, -1); + if (methodExists == MemberExistsResult.EXISTS_BY_LOMBOK) methodExists = methodExists(job.buildMethodName, job.builderType, 0); if (methodExists == MemberExistsResult.NOT_EXISTS) { - MethodDeclaration md = generateBuildMethod(cfv, tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast, accessForInners); - if (md != null) injectMethod(builderType, md); + MethodDeclaration md = generateBuildMethod(job, nameOfBuilderMethod, buildMethodReturnType, buildMethodThrownExceptions, addCleaning); + if (md != null) injectMethod(job.builderType, md); } } - if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { + if (methodExists("toString", job.builderType, 0) == MemberExistsResult.NOT_EXISTS) { List<Included<EclipseNode, ToString.Include>> fieldNodes = new ArrayList<Included<EclipseNode, ToString.Include>>(); - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { for (EclipseNode f : bfd.createdFields) { fieldNodes.add(new Included<EclipseNode, ToString.Include>(f, null, true, false)); } } - MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD); - if (md != null) injectMethod(builderType, md); + MethodDeclaration md = HandleToString.createToString(job.builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD); + if (md != null) injectMethod(job.builderType, md); } if (addCleaning) { - MethodDeclaration cleanMethod = generateCleanMethod(builderFields, builderType, ast); - if (cleanMethod != null) injectMethod(builderType, cleanMethod); + MethodDeclaration cleanMethod = generateCleanMethod(job); + if (cleanMethod != null) injectMethod(job.builderType, cleanMethod); } - if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; + if (generateBuilderMethod && methodExists(job.builderMethodName, job.parentType, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; if (generateBuilderMethod) { - MethodDeclaration md = generateBuilderMethod(cfv, isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast, accessForOuters); - if (md != null) injectMethod(tdParent, md); + MethodDeclaration md = generateBuilderMethod(job); + if (md != null) injectMethod(job.parentType, md); } - if (toBuilder) switch (methodExists(toBuilderMethodName, tdParent, 0)) { + if (job.toBuilder) switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, job.parentType, 0)) { case EXISTS_BY_USER: annotationNode.addWarning("Not generating toBuilder() as it already exists."); break; case NOT_EXISTS: - TypeParameter[] tps = typeParams; + TypeParameter[] tps = job.typeParams; if (typeArgsForToBuilder != null) { tps = new TypeParameter[typeArgsForToBuilder.size()]; for (int i = 0; i < tps.length; i++) { @@ -519,9 +590,9 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { tps[i].name = typeArgsForToBuilder.get(i); } } - MethodDeclaration md = generateToBuilderMethod(cfv, isStatic, toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast, accessForOuters, builderInstance.setterPrefix()); + MethodDeclaration md = generateToBuilderMethod(job, tps, annInstance.setterPrefix()); - if (md != null) injectMethod(tdParent, md); + if (md != null) injectMethod(job.parentType, md); } if (nonFinalNonDefaultedFields != null && generateBuilderMethod) { @@ -530,7 +601,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } } } - + static char[] returnTypeToBuilderClassName(EclipseNode annotationNode, MethodDeclaration md, TypeParameter[] typeParams) { char[] token; if (md.returnType instanceof QualifiedTypeReference) { @@ -560,26 +631,31 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return token; } - private static final char[] BUILDER_TEMP_VAR = {'b', 'u', 'i', 'l', 'd', 'e', 'r'}; - private MethodDeclaration generateToBuilderMethod(CheckerFrameworkVersion cfv, boolean isStatic, String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, boolean fluent, ASTNode source, AccessLevel access, String prefix) { - int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long) pS << 32 | pE; + private MethodDeclaration generateToBuilderMethod(BuilderJob job, TypeParameter[] typeParameters, String prefix) { + int pS = job.source.sourceStart, pE = job.source.sourceEnd; + long p = job.getPos(); - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); - out.selector = methodName.toCharArray(); - out.modifiers = toEclipseModifier(access); + MethodDeclaration out = job.createNewMethodDeclaration(); + out.selector = TO_BUILDER_METHOD_NAME; + out.modifiers = toEclipseModifier(job.accessOuters); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - out.returnType = namePlusTypeParamsToTypeReference(type, builderClassName.toCharArray(), !isStatic, typeParams, p); + + out.returnType = job.createBuilderTypeReference(); + if (job.checkerFramework.generateUnique()) { + int len = out.returnType.getTypeName().length; + out.returnType.annotations = new Annotation[len][]; + out.returnType.annotations[len - 1] = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__UNIQUE)}; + } AllocationExpression invoke = new AllocationExpression(); - invoke.type = namePlusTypeParamsToTypeReference(type, builderClassName.toCharArray(), !isStatic, typeParams, p); + invoke.type = job.createBuilderTypeReference(); Expression receiver = invoke; List<Statement> preStatements = null; List<Statement> postStatements = null; - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { String setterName = new String(bfd.name); - String setterPrefix = !prefix.isEmpty() ? prefix : fluent ? "" : "set"; + String setterPrefix = !prefix.isEmpty() ? prefix : job.oldFluent ? "" : "set"; if (!setterPrefix.isEmpty()) setterName = HandlerUtil.buildAccessorName(setterPrefix, setterName); MessageSend ms = new MessageSend(); @@ -597,13 +673,13 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { boolean obtainIsStatic = bfd.obtainVia.isStatic(); MessageSend obtainExpr = new MessageSend(); if (obtainIsStatic) { - if (typeParams != null && typeParams.length > 0) { - obtainExpr.typeArguments = new TypeReference[typeParams.length]; - for (int j = 0; j<typeParams.length; j++) { - obtainExpr.typeArguments[j] = new SingleTypeReference(typeParams[j].name, 0); + if (typeParameters != null && typeParameters.length > 0) { + obtainExpr.typeArguments = new TypeReference[typeParameters.length]; + for (int j = 0; j < typeParameters.length; j++) { + obtainExpr.typeArguments[j] = new SingleTypeReference(typeParameters[j].name, 0); } } - obtainExpr.receiver = generateNameReference(type, 0); + obtainExpr.receiver = generateNameReference(job.parentType, 0); } else { obtainExpr.receiver = new ThisReference(0, 0); } @@ -616,7 +692,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { // for ecj so we match what javac's handler does. LocalDeclaration ld = new LocalDeclaration(bfd.name, 0, 0); ld.modifiers = ClassFileConstants.AccFinal; - ld.type = EclipseHandlerUtil.copyType(bfd.type, source); + ld.type = EclipseHandlerUtil.copyType(bfd.type, job.source); ld.initialization = obtainExpr; if (preStatements == null) preStatements = new ArrayList<Statement>(); preStatements.add(ld); @@ -645,7 +721,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { LocalDeclaration b = new LocalDeclaration(BUILDER_TEMP_VAR, pS, pE); out.statements[preSs] = b; b.modifiers |= ClassFileConstants.AccFinal; - b.type = namePlusTypeParamsToTypeReference(type, builderClassName.toCharArray(), !isStatic, typeParams, p); + b.type = job.createBuilderTypeReference(); b.type.sourceStart = pS; b.type.sourceEnd = pE; b.initialization = receiver; out.statements[preSs + postSs + 1] = new ReturnStatement(new SingleNameReference(BUILDER_TEMP_VAR, p), pS, pE); @@ -655,69 +731,81 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { out.statements[preSs] = new ReturnStatement(receiver, pS, pE); } - if (cfv.generateUnique()) { - out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)}; - } - - createRelevantNonNullAnnotation(type, out); - out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); + createRelevantNonNullAnnotation(job.parentType, out); + out.traverse(new SetGeneratedByVisitor(job.source), ((TypeDeclaration) job.parentType.get()).scope); return out; } - private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType, ASTNode source) { + private MethodDeclaration generateCleanMethod(BuilderJob job) { List<Statement> statements = new ArrayList<Statement>(); - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, builderType, statements); + bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, job.builderType, statements); } } FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); thisUnclean.receiver = new ThisReference(0, 0); statements.add(new Assignment(thisUnclean, new FalseLiteral(0, 0), 0)); - MethodDeclaration decl = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); + MethodDeclaration decl = job.createNewMethodDeclaration(); decl.selector = CLEAN_METHOD_NAME; decl.modifiers = ClassFileConstants.AccPrivate; decl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; decl.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0); decl.statements = statements.toArray(new Statement[0]); - decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + decl.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null); return decl; } - static Receiver generateBuildReceiver(CheckerFrameworkVersion cfv, EclipseNode type, List<BuilderFieldData> builderFields, ASTNode source) { - if (!cfv.generateCalledMethods()) return null; + static Receiver generateNotCalledReceiver(BuilderJob job, String setterName) { + char[][] nameNotCalled = fromQualifiedName(CheckerFrameworkVersion.NAME__NOT_CALLED); + SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameNotCalled, poss(job.source, nameNotCalled.length)), job.source.sourceStart); + ann.memberValue = new StringLiteral(setterName.toCharArray(), 0, 0, 0); + + TypeReference typeReference = job.createBuilderTypeReference(); + int trLen = typeReference.getTypeName().length; + typeReference.annotations = new Annotation[trLen][]; + typeReference.annotations[trLen - 1] = new Annotation[] {ann}; + return new Receiver(new char[] { 't', 'h', 'i', 's' }, 0, typeReference, null, 0); + } + + static Receiver generateBuildReceiver(BuilderJob job) { + if (!job.checkerFramework.generateCalledMethods()) return null; List<char[]> mandatories = new ArrayList<char[]>(); - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData == null && bfd.nameOfSetFlag == null) mandatories.add(bfd.name); } if (mandatories.size() == 0) return null; + + int pS = job.source.sourceStart, pE = job.source.sourceEnd; + char[][] nameCalled = fromQualifiedName(CheckerFrameworkVersion.NAME__CALLED); - SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameCalled, poss(source, nameCalled.length)), source.sourceStart); + SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameCalled, poss(job.source, nameCalled.length)), pS); if (mandatories.size() == 1) { ann.memberValue = new StringLiteral(mandatories.get(0), 0, 0, 0); } else { ArrayInitializer arr = new ArrayInitializer(); - arr.sourceStart = source.sourceStart; - arr.sourceEnd = source.sourceEnd; + arr.sourceStart = pS; + arr.sourceEnd = pE; arr.expressions = new Expression[mandatories.size()]; for (int i = 0; i < arr.expressions.length; i++) { - arr.expressions[i] = new StringLiteral(mandatories.get(i), source.sourceStart, source.sourceEnd, 0); + arr.expressions[i] = new StringLiteral(mandatories.get(i), pS, pE, 0); } ann.memberValue = arr; } - - QualifiedTypeReference typeReference = (QualifiedTypeReference) generateTypeReference(type, source.sourceStart); - typeReference.annotations = new Annotation[typeReference.tokens.length][]; - typeReference.annotations[0] = new Annotation[] {ann}; - return new Receiver(new char[] { 't', 'h', 'i', 's' }, 0, typeReference, null, Modifier.FINAL); + + TypeReference typeReference = job.createBuilderTypeReference(); + int len = typeReference.getTypeName().length; + typeReference.annotations = new Annotation[len][]; + typeReference.annotations[len - 1] = new Annotation[] {ann}; + return new Receiver(new char[] { 't', 'h', 'i', 's' }, 0, typeReference, null, 0); } - public MethodDeclaration generateBuildMethod(CheckerFrameworkVersion cfv, EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source, AccessLevel access) { - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); + public MethodDeclaration generateBuildMethod(BuilderJob job, char[] staticName, TypeReference returnType, TypeReference[] thrownExceptions, boolean addCleaning) { + MethodDeclaration out = job.createNewMethodDeclaration(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List<Statement> statements = new ArrayList<Statement>(); @@ -730,14 +818,14 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { statements.add(new IfStatement(notClean, invokeClean, 0, 0)); } - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.builderFieldName, "this"); + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, job.builderType, statements, bfd.builderFieldName, "this"); } } List<Expression> args = new ArrayList<Expression>(); - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.nameOfSetFlag != null) { LocalDeclaration ld = new LocalDeclaration(bfd.builderFieldName, 0, 0); ld.type = copyType(bfd.type); @@ -747,11 +835,11 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { statements.add(ld); MessageSend inv = new MessageSend(); - inv.sourceStart = source.sourceStart; - inv.sourceEnd = source.sourceEnd; - inv.receiver = new SingleNameReference(((TypeDeclaration) tdParent.get()).name, 0L); + inv.sourceStart = job.source.sourceStart; + inv.sourceEnd = job.source.sourceEnd; + inv.receiver = new SingleNameReference(((TypeDeclaration) job.parentType.get()).name, 0L); inv.selector = bfd.nameOfDefaultProvider; - inv.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters); + inv.typeArguments = typeParameterNames(((TypeDeclaration) job.builderType.get()).typeParameters); Assignment defaultAssign = new Assignment(new SingleNameReference(bfd.builderFieldName, 0L), inv, 0); FieldReference thisSet = new FieldReference(bfd.nameOfSetFlag, 0L); @@ -775,8 +863,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { statements.add(new Assignment(thisUnclean, new TrueLiteral(0, 0), 0)); } - out.modifiers = toEclipseModifier(access); - out.selector = name.toCharArray(); + out.modifiers = toEclipseModifier(job.accessInners); + out.selector = job.buildMethodName.toCharArray(); out.thrownExceptions = copyTypes(thrownExceptions); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = returnType; @@ -789,13 +877,13 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } else { MessageSend invoke = new MessageSend(); invoke.selector = staticName; - if (isStatic) { - invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0); + if (job.isStatic) { + invoke.receiver = new SingleNameReference(job.builderType.up().getName().toCharArray(), 0); } else { - invoke.receiver = new QualifiedThisReference(generateTypeReference(type.up(), 0) , 0, 0); + invoke.receiver = new QualifiedThisReference(generateTypeReference(job.builderType.up(), 0) , 0, 0); } - invoke.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters); + invoke.typeArguments = typeParameterNames(((TypeDeclaration) job.builderType.get()).typeParameters); invoke.arguments = args.isEmpty() ? null : args.toArray(new Expression[0]); if (returnType instanceof SingleTypeReference && Arrays.equals(TypeConstants.VOID, ((SingleTypeReference) returnType).token)) { statements.add(invoke); @@ -804,12 +892,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } } out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[0]); - if (cfv.generateSideEffectFree()) { - out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE)}; + if (job.checkerFramework.generateSideEffectFree()) { + out.annotations = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE)}; } - out.receiver = generateBuildReceiver(cfv, type, builderFields, source); - if (staticName == null) createRelevantNonNullAnnotation(type, out); - out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + out.receiver = generateBuildReceiver(job); + if (staticName == null) createRelevantNonNullAnnotation(job.builderType, out); + out.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null); return out; } @@ -840,57 +928,55 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return out; } - public MethodDeclaration generateBuilderMethod(CheckerFrameworkVersion cfv, boolean isStatic, String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source, AccessLevel access) { - int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long) pS << 32 | pE; - char[] builderClassName_ = builderClassName.toCharArray(); + public MethodDeclaration generateBuilderMethod(BuilderJob job) { + int pS = job.source.sourceStart, pE = job.source.sourceEnd; + long p = job.getPos(); - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); - out.selector = builderMethodName.toCharArray(); - out.modifiers = toEclipseModifier(access); - if (isStatic) out.modifiers |= ClassFileConstants.AccStatic; + MethodDeclaration out = job.createNewMethodDeclaration(); + out.selector = job.builderMethodName.toCharArray(); + out.modifiers = toEclipseModifier(job.accessOuters); + if (job.isStatic) out.modifiers |= ClassFileConstants.AccStatic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - out.returnType = namePlusTypeParamsToTypeReference(type, builderClassName_, !isStatic, typeParams, p); - out.typeParameters = copyTypeParams(typeParams, source); + out.returnType = job.createBuilderTypeReference(); + if (job.checkerFramework.generateUnique()) { + int len = out.returnType.getTypeName().length; + out.returnType.annotations = new Annotation[len][]; + out.returnType.annotations[len - 1] = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__UNIQUE)}; + } + out.typeParameters = job.copyTypeParams(); AllocationExpression invoke = new AllocationExpression(); - if (isStatic) { - invoke.type = namePlusTypeParamsToTypeReference(type, builderClassName_, false, typeParams, p); + if (job.isStatic) { + invoke.type = job.createBuilderTypeReferenceForceStatic(); out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)}; } else { // return this.new Builder(); QualifiedAllocationExpression qualifiedInvoke = new QualifiedAllocationExpression(); qualifiedInvoke.enclosingInstance = new ThisReference(pS, pE); - if (typeParams == null || typeParams.length == 0) { - qualifiedInvoke.type = new SingleTypeReference(builderClassName_, p); + if (job.typeParams == null || job.typeParams.length == 0) { + qualifiedInvoke.type = new SingleTypeReference(job.builderClassNameArr, p); } else { - qualifiedInvoke.type = namePlusTypeParamsToTypeReference(null, builderClassName_, false, typeParams, p); + qualifiedInvoke.type = namePlusTypeParamsToTypeReference(null, job.builderClassNameArr, false, job.typeParams, p); } out.statements = new Statement[] {new ReturnStatement(qualifiedInvoke, pS, pE)}; } - Annotation uniqueAnn = cfv.generateUnique() ? generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE) : null; - Annotation sefAnn = cfv.generateSideEffectFree() ? generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) : null; - if (uniqueAnn != null && sefAnn != null) { - out.annotations = new Annotation[] {uniqueAnn, sefAnn}; - } else if (uniqueAnn != null) { - out.annotations = new Annotation[] {uniqueAnn}; - } else if (sefAnn != null) { - out.annotations = new Annotation[] {sefAnn}; + if (job.checkerFramework.generateSideEffectFree()) { + out.annotations = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE)}; } - createRelevantNonNullAnnotation(type, out); - out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); + createRelevantNonNullAnnotation(job.builderType, out); + out.traverse(new SetGeneratedByVisitor(job.source), ((TypeDeclaration) job.builderType.get()).scope); return out; } - public void generateBuilderFields(EclipseNode builderType, List<BuilderFieldData> builderFields, ASTNode source) { + public void generateBuilderFields(BuilderJob job) { List<EclipseNode> existing = new ArrayList<EclipseNode>(); - for (EclipseNode child : builderType.down()) { + for (EclipseNode child : job.builderType.down()) { if (child.getKind() == Kind.FIELD) existing.add(child); } - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType)); + bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, job.builderType)); } else { EclipseNode field = null, setFlag = null; for (EclipseNode exists : existing) { @@ -904,45 +990,43 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; fd.modifiers = ClassFileConstants.AccPrivate; fd.type = copyType(bfd.type); - fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); - field = injectFieldAndMarkGenerated(builderType, fd); + fd.traverse(new SetGeneratedByVisitor(job.source), (MethodScope) null); + field = injectFieldAndMarkGenerated(job.builderType, fd); } if (setFlag == null && bfd.nameOfSetFlag != null) { FieldDeclaration fd = new FieldDeclaration(bfd.nameOfSetFlag, 0, 0); fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; fd.modifiers = ClassFileConstants.AccPrivate; fd.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); - fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); - injectFieldAndMarkGenerated(builderType, fd); + fd.traverse(new SetGeneratedByVisitor(job.source), (MethodScope) null); + injectFieldAndMarkGenerated(job.builderType, fd); } bfd.createdFields.add(field); } } } - private static final AbstractMethodDeclaration[] EMPTY = {}; - - public void makePrefixedSetterMethodsForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain, AccessLevel access, EclipseNode originalFieldNode, String prefix) { + public void makePrefixedSetterMethodsForBuilder(BuilderJob job, BuilderFieldData bfd, String prefix) { boolean deprecate = isFieldDeprecated(bfd.originalFieldNode); if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - makePrefixedSetterMethodForBuilder(cfv, builderType, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, sourceNode, fluent, chain, bfd.annotations, access, originalFieldNode, prefix); + makePrefixedSetterMethodForBuilder(job, bfd, deprecate, prefix); } else { - bfd.singularData.getSingularizer().generateMethods(cfv, bfd.singularData, deprecate, builderType, fluent, chain, access); + bfd.singularData.getSingularizer().generateMethods(job, bfd.singularData, deprecate); } } - private void makePrefixedSetterMethodForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain, Annotation[] annotations, AccessLevel access, EclipseNode originalFieldNode, String prefix) { - TypeDeclaration td = (TypeDeclaration) builderType.get(); + private void makePrefixedSetterMethodForBuilder(BuilderJob job, BuilderFieldData bfd, boolean deprecate, String prefix) { + TypeDeclaration td = (TypeDeclaration) job.builderType.get(); + EclipseNode fieldNode = bfd.createdFields.get(0); AbstractMethodDeclaration[] existing = td.methods; - if (existing == null) existing = EMPTY; + if (existing == null) existing = EMPTY_METHODS; int len = existing.length; - String setterPrefix = prefix.isEmpty() ? "set" : prefix; String setterName; - if(fluent) { - setterName = prefix.isEmpty() ? new String(paramName) : HandlerUtil.buildAccessorName(setterPrefix, new String(paramName)); + if (job.oldFluent) { + setterName = prefix.isEmpty() ? new String(bfd.name) : HandlerUtil.buildAccessorName(setterPrefix, new String(bfd.name)); } else { - setterName = HandlerUtil.buildAccessorName(setterPrefix, new String(paramName)); + setterName = HandlerUtil.buildAccessorName(setterPrefix, new String(bfd.name)); } for (int i = 0; i < len; i++) { @@ -952,34 +1036,39 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } List<Annotation> methodAnnsList = Collections.<Annotation>emptyList(); - Annotation[] methodAnns = EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); + Annotation[] methodAnns = EclipseHandlerUtil.findCopyableToSetterAnnotations(bfd.originalFieldNode); if (methodAnns != null && methodAnns.length > 0) methodAnnsList = Arrays.asList(methodAnns); - ASTNode source = sourceNode.get(); - MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, paramName, nameOfSetFlag, chain, toEclipseModifier(access), - sourceNode, methodAnnsList, annotations != null ? Arrays.asList(copyAnnotations(source, annotations)) : Collections.<Annotation>emptyList()); - if (cfv.generateCalledMethods()) { - char[][] nameNotCalled = fromQualifiedName(CheckerFrameworkVersion.NAME__NOT_CALLED); - SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameNotCalled, poss(source, nameNotCalled.length)), source.sourceStart); - ann.memberValue = new StringLiteral(setterName.toCharArray(), 0, 0, 0); - - QualifiedTypeReference typeReference = (QualifiedTypeReference) generateTypeReference(builderType, 0); - typeReference.annotations = new Annotation[typeReference.tokens.length][]; - typeReference.annotations[0] = new Annotation[] {ann}; - setter.receiver = new Receiver(new char[] { 't', 'h', 'i', 's' }, 0, typeReference, null, Modifier.FINAL); + ASTNode source = job.sourceNode.get(); + MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, bfd.name, bfd.nameOfSetFlag, job.oldChain, toEclipseModifier(job.accessInners), + job.sourceNode, methodAnnsList, bfd.annotations != null ? Arrays.asList(copyAnnotations(source, bfd.annotations)) : Collections.<Annotation>emptyList()); + if (job.checkerFramework.generateCalledMethods()) setter.receiver = generateNotCalledReceiver(job, setterName); + if (job.sourceNode.up().getKind() == Kind.METHOD) { + copyJavadocFromParam(bfd.originalFieldNode.up(), setter, td, bfd.name.toString()); + } else { + copyJavadoc(bfd.originalFieldNode, setter, td, CopyJavadoc.SETTER, true); } - injectMethod(builderType, setter); + injectMethod(job.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()); + String newJavadoc = addReturnsThisIfNeeded(getParamJavadoc(methodComment, param)); + 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(); + public void makeBuilderClass(BuilderJob job) { + TypeDeclaration parent = (TypeDeclaration) job.parentType.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; - builder.modifiers |= toEclipseModifier(access); - if (isStatic) builder.modifiers |= ClassFileConstants.AccStatic; - builder.typeParameters = copyTypeParams(typeParams, source); - builder.name = builderClassName.toCharArray(); - builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); - return injectType(tdParent, builder); + builder.modifiers |= toEclipseModifier(job.accessOuters); + if (job.isStatic) builder.modifiers |= ClassFileConstants.AccStatic; + builder.typeParameters = job.copyTypeParams(); + builder.name = job.builderClassNameArr; + builder.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null); + job.builderType = injectType(job.parentType, builder); } private void addObtainVia(BuilderFieldData bfd, EclipseNode node) { diff --git a/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java index d0c597fd..464a0efd 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2018 The Project Lombok Authors. + * Copyright (C) 2017-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,6 @@ package lombok.eclipse.handlers; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.mangosdk.spi.ProviderFor; import lombok.Builder; import lombok.core.AST.Kind; @@ -32,8 +31,9 @@ import lombok.core.HandlerPriority; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.SuperBuilder; +import lombok.spi.Provides; -@ProviderFor(EclipseAnnotationHandler.class) +@Provides @HandlerPriority(-1025) //HandleBuilder's level, minus one. public class HandleBuilderDefault extends EclipseAnnotationHandler<Builder.Default> { @Override public void handle(AnnotationValues<Builder.Default> annotation, Annotation ast, EclipseNode annotationNode) { diff --git a/src/core/lombok/eclipse/handlers/HandleCleanup.java b/src/core/lombok/eclipse/handlers/HandleCleanup.java index dde7cd08..3ce41763 100644 --- a/src/core/lombok/eclipse/handlers/HandleCleanup.java +++ b/src/core/lombok/eclipse/handlers/HandleCleanup.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,6 +32,7 @@ import lombok.core.AnnotationValues; import lombok.core.AST.Kind; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -52,12 +53,11 @@ import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.SwitchStatement; import org.eclipse.jdt.internal.compiler.ast.TryStatement; -import org.mangosdk.spi.ProviderFor; /** * Handles the {@code lombok.Cleanup} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleCleanup extends EclipseAnnotationHandler<Cleanup> { public void handle(AnnotationValues<Cleanup> annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.CLEANUP_FLAG_USAGE, "@Cleanup"); diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index ff81b763..e69c3267 100755 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2020 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -44,6 +44,7 @@ import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -79,10 +80,9 @@ 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.lookup.TypeConstants; -import org.mangosdk.spi.ProviderFor; public class HandleConstructor { - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleNoArgsConstructor extends EclipseAnnotationHandler<NoArgsConstructor> { private static final String NAME = NoArgsConstructor.class.getSimpleName(); private HandleConstructor handleConstructor = new HandleConstructor(); @@ -105,7 +105,7 @@ public class HandleConstructor { } } - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleRequiredArgsConstructor extends EclipseAnnotationHandler<RequiredArgsConstructor> { private static final String NAME = RequiredArgsConstructor.class.getSimpleName(); private HandleConstructor handleConstructor = new HandleConstructor(); @@ -166,7 +166,7 @@ public class HandleConstructor { return fields; } - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleAllArgsConstructor extends EclipseAnnotationHandler<AllArgsConstructor> { private static final String NAME = AllArgsConstructor.class.getSimpleName(); @@ -456,14 +456,12 @@ public class HandleConstructor { constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[0]); /* Generate annotations that must be put on the generated method, and attach them. */ { - Annotation[] constructorProperties = null, checkerFramework = null; + Annotation[] constructorProperties = null; if (addConstructorProperties && !isLocalType(type)) constructorProperties = createConstructorProperties(source, fieldsToParam); - if (getCheckerFrameworkVersion(type).generateUnique()) checkerFramework = new Annotation[] { generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE) }; constructor.annotations = copyAnnotations(source, onConstructor.toArray(new Annotation[0]), - constructorProperties, - checkerFramework); + constructorProperties); } constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); @@ -531,6 +529,11 @@ public class HandleConstructor { TypeDeclaration typeDecl = (TypeDeclaration) type.get(); constructor.returnType = EclipseHandlerUtil.namePlusTypeParamsToTypeReference(type, typeDecl.typeParameters, p); constructor.annotations = null; + if (getCheckerFrameworkVersion(type).generateUnique()) { + int len = constructor.returnType.getTypeName().length; + constructor.returnType.annotations = new Annotation[len][]; + constructor.returnType.annotations[len - 1] = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)}; + } constructor.selector = name.toCharArray(); constructor.thrownExceptions = null; constructor.typeParameters = copyTypeParams(((TypeDeclaration) type.get()).typeParameters, source); @@ -551,9 +554,7 @@ public class HandleConstructor { assigns.add(nameRef); Argument parameter = new Argument(field.name, fieldPos, copyType(field.type, source), Modifier.FINAL); - Annotation[] checkerFramework = null; - if (getCheckerFrameworkVersion(fieldNode).generateUnique()) checkerFramework = new Annotation[] { generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE) }; - parameter.annotations = copyAnnotations(source, findCopyableAnnotations(fieldNode), checkerFramework); + parameter.annotations = copyAnnotations(source, findCopyableAnnotations(fieldNode)); params.add(parameter); } diff --git a/src/core/lombok/eclipse/handlers/HandleData.java b/src/core/lombok/eclipse/handlers/HandleData.java index 6663d7d0..00e1fd38 100644 --- a/src/core/lombok/eclipse/handlers/HandleData.java +++ b/src/core/lombok/eclipse/handlers/HandleData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,7 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.*; -import static lombok.eclipse.handlers.EclipseHandlerUtil.isClass; +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.util.Collections; @@ -33,14 +33,14 @@ import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.mangosdk.spi.ProviderFor; /** * Handles the {@code lombok.Data} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleData extends EclipseAnnotationHandler<Data> { private HandleGetter handleGetter = new HandleGetter(); private HandleSetter handleSetter = new HandleSetter(); diff --git a/src/core/lombok/eclipse/handlers/HandleDelegate.java b/src/core/lombok/eclipse/handlers/HandleDelegate.java index 9d352558..0e3eab66 100644 --- a/src/core/lombok/eclipse/handlers/HandleDelegate.java +++ b/src/core/lombok/eclipse/handlers/HandleDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Project Lombok Authors. + * Copyright (C) 2014-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,14 +27,14 @@ import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.Delegate; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.mangosdk.spi.ProviderFor; /** * This class just handles basic error cases. The real meat of eclipse '@Delegate' support is in {@code PatchDelegate}. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleDelegate extends EclipseAnnotationHandler<Delegate> { public void handle(AnnotationValues<Delegate> annotation, Annotation ast, EclipseNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.DELEGATE_FLAG_USAGE, "@Delegate"); diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index 83e6de61..753c489c 100755 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,7 @@ import java.util.Set; import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.EqualsAndHashCode; +import lombok.EqualsAndHashCode.CacheStrategy; import lombok.core.AST.Kind; import lombok.core.handlers.HandlerUtil; import lombok.core.handlers.InclusionExclusionUtils; @@ -47,6 +48,7 @@ import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -59,6 +61,8 @@ import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression; import org.eclipse.jdt.internal.compiler.ast.IntLiteral; @@ -86,14 +90,16 @@ import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; -import org.mangosdk.spi.ProviderFor; /** * Handles the {@code EqualsAndHashCode} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndHashCode> { + private static final String HASH_CODE_CACHE_NAME = "$hashCodeCache"; + + private final char[] HASH_CODE_CACHE_NAME_ARR = HASH_CODE_CACHE_NAME.toCharArray(); private final char[] PRIME = "PRIME".toCharArray(); private final char[] RESULT = "result".toCharArray(); @@ -116,7 +122,9 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; - generateMethods(annotationNode.up(), annotationNode, members, callSuper, true, fieldAccess, onParam); + boolean cacheHashCode = ann.cacheStrategy() == CacheStrategy.LAZY; + + generateMethods(annotationNode.up(), annotationNode, members, callSuper, true, cacheHashCode, fieldAccess, onParam); } public void generateEqualsAndHashCodeForType(EclipseNode typeNode, EclipseNode errorNode) { @@ -130,20 +138,18 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS); FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; - generateMethods(typeNode, errorNode, members, null, false, access, new ArrayList<Annotation>()); + generateMethods(typeNode, errorNode, members, null, false, false, access, new ArrayList<Annotation>()); } public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<Included<EclipseNode, EqualsAndHashCode.Include>> members, - Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) { - - TypeDeclaration typeDecl = null; - if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); + Boolean callSuper, boolean whineIfExists, boolean cacheHashCode, FieldAccess fieldAccess, List<Annotation> onParam) { if (!isClass(typeNode)) { errorNode.addError("@EqualsAndHashCode is only supported on a class."); return; } + TypeDeclaration typeDecl = (TypeDeclaration) typeNode.get(); boolean implicitCallSuper = callSuper == null; if (callSuper == null) { @@ -156,30 +162,6 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH boolean isDirectDescendantOfObject = isDirectDescendantOfObject(typeNode); - if (isDirectDescendantOfObject && callSuper) { - errorNode.addError("Generating equals/hashCode with a supercall to java.lang.Object is pointless."); - return; - } - - if (implicitCallSuper && !isDirectDescendantOfObject) { - CallSuperType cst = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_CALL_SUPER); - if (cst == null) cst = CallSuperType.WARN; - - switch (cst) { - default: - case WARN: - errorNode.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type."); - callSuper = false; - break; - case SKIP: - callSuper = false; - break; - case CALL: - callSuper = true; - break; - } - } - boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0; boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject; MemberExistsResult equalsExists = methodExists("equals", typeNode, 1); @@ -208,6 +190,30 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH //fallthrough } + if (isDirectDescendantOfObject && callSuper) { + errorNode.addError("Generating equals/hashCode with a supercall to java.lang.Object is pointless."); + return; + } + + if (implicitCallSuper && !isDirectDescendantOfObject) { + CallSuperType cst = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_CALL_SUPER); + if (cst == null) cst = CallSuperType.WARN; + + switch (cst) { + default: + case WARN: + errorNode.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type."); + callSuper = false; + break; + case SKIP: + callSuper = false; + break; + case CALL: + callSuper = true; + break; + } + } + MethodDeclaration equalsMethod = createEquals(typeNode, members, callSuper, errorNode.get(), fieldAccess, needsCanEqual, onParam); equalsMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration)typeNode.get()).scope); injectMethod(typeNode, equalsMethod); @@ -218,12 +224,33 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH injectMethod(typeNode, canEqualMethod); } - MethodDeclaration hashCodeMethod = createHashCode(typeNode, members, callSuper, errorNode.get(), fieldAccess); + if (cacheHashCode){ + if (fieldExists(HASH_CODE_CACHE_NAME, typeNode) != MemberExistsResult.NOT_EXISTS) { + String msg = String.format("Not caching the result of hashCode: A field named %s already exists.", HASH_CODE_CACHE_NAME); + errorNode.addWarning(msg); + cacheHashCode = false; + } else { + createHashCodeCacheField(typeNode, errorNode.get()); + } + } + + MethodDeclaration hashCodeMethod = createHashCode(typeNode, members, callSuper, cacheHashCode, errorNode.get(), fieldAccess); hashCodeMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration)typeNode.get()).scope); injectMethod(typeNode, hashCodeMethod); } + + private void createHashCodeCacheField(EclipseNode typeNode, ASTNode source) { + FieldDeclaration hashCodeCacheDecl = new FieldDeclaration(HASH_CODE_CACHE_NAME_ARR, 0, 0); + hashCodeCacheDecl.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccTransient; + hashCodeCacheDecl.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + hashCodeCacheDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0); + hashCodeCacheDecl.declarationSourceEnd = -1; + injectFieldAndMarkGenerated(typeNode, hashCodeCacheDecl); + setGeneratedBy(hashCodeCacheDecl, source); + setGeneratedBy(hashCodeCacheDecl.type, source); + } - public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess) { + public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, boolean cacheHashCode, ASTNode source, FieldAccess fieldAccess) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; @@ -234,7 +261,10 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH method.returnType = TypeReference.baseTypeReference(TypeIds.T_int, 0); setGeneratedBy(method.returnType, source); Annotation overrideAnnotation = makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source); - if (getCheckerFrameworkVersion(type).generateSideEffectFree()) { + CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(type); + if (cacheHashCode && checkerFramework.generatePure()) { + method.annotations = new Annotation[] { overrideAnnotation, generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__PURE) }; + } else if (checkerFramework.generateSideEffectFree()) { method.annotations = new Annotation[] { overrideAnnotation, generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) }; } else { method.annotations = new Annotation[] { overrideAnnotation }; @@ -258,6 +288,22 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH } } + /* if (this.$hashCodeCache != 0) return this.$hashCodeCache; */ { + if (cacheHashCode) { + FieldReference hashCodeCacheRef = new FieldReference(HASH_CODE_CACHE_NAME_ARR, p); + hashCodeCacheRef.receiver = new ThisReference(pS, pE); + setGeneratedBy(hashCodeCacheRef, source); + setGeneratedBy(hashCodeCacheRef.receiver, source); + EqualExpression cacheNotZero = new EqualExpression(hashCodeCacheRef, makeIntLiteral("0".toCharArray(), source), OperatorIds.NOT_EQUAL); + setGeneratedBy(cacheNotZero, source); + ReturnStatement returnCache = new ReturnStatement(hashCodeCacheRef, pS, pE); + setGeneratedBy(returnCache, source); + IfStatement ifStatement = new IfStatement(cacheNotZero, returnCache, pS, pE); + setGeneratedBy(ifStatement, source); + statements.add(ifStatement); + } + } + /* final int PRIME = X; */ { /* Without members, PRIME isn't used, as that would trigger a 'local variable not used' warning. */ if (!isEmpty) { @@ -292,7 +338,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH resultDecl.initialization = init; resultDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0); resultDecl.type.sourceStart = pS; resultDecl.type.sourceEnd = pE; - if (isEmpty) resultDecl.modifiers |= Modifier.FINAL; + if (isEmpty && !cacheHashCode) resultDecl.modifiers |= Modifier.FINAL; setGeneratedBy(resultDecl.type, source); statements.add(resultDecl); } @@ -387,6 +433,49 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH } } + /* + * if (result == 0) result = Integer.MIN_VALUE; + * this.$hashCodeCache = result; + * + */ { + if (cacheHashCode) { + SingleNameReference resultRef = new SingleNameReference(RESULT, p); + setGeneratedBy(resultRef, source); + + EqualExpression resultIsZero = new EqualExpression(resultRef, makeIntLiteral("0".toCharArray(), source), OperatorIds.EQUAL_EQUAL); + setGeneratedBy(resultIsZero, source); + + resultRef = new SingleNameReference(RESULT, p); + setGeneratedBy(resultRef, source); + + FieldReference integerMinValue = new FieldReference("MIN_VALUE".toCharArray(), p); + integerMinValue.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_INTEGER); + setGeneratedBy(integerMinValue, source); + + Assignment newResult = new Assignment(resultRef, integerMinValue, pE); + newResult.sourceStart = pS; newResult.statementEnd = newResult.sourceEnd = pE; + setGeneratedBy(newResult, source); + + IfStatement ifStatement = new IfStatement(resultIsZero, newResult, pS, pE); + setGeneratedBy(ifStatement, source); + statements.add(ifStatement); + + + FieldReference hashCodeCacheRef = new FieldReference(HASH_CODE_CACHE_NAME_ARR, p); + hashCodeCacheRef.receiver = new ThisReference(pS, pE); + setGeneratedBy(hashCodeCacheRef, source); + setGeneratedBy(hashCodeCacheRef.receiver, source); + + resultRef = new SingleNameReference(RESULT, p); + setGeneratedBy(resultRef, source); + + Assignment cacheResult = new Assignment(hashCodeCacheRef, resultRef, pE); + cacheResult.sourceStart = pS; cacheResult.statementEnd = cacheResult.sourceEnd = pE; + setGeneratedBy(cacheResult, source); + statements.add(cacheResult); + } + } + /* return result; */ { SingleNameReference resultRef = new SingleNameReference(RESULT, p); setGeneratedBy(resultRef, source); diff --git a/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java b/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java index 8e53d873..5857780c 100644 --- a/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java +++ b/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,7 +28,6 @@ import java.util.List; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.mangosdk.spi.ProviderFor; import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; @@ -36,9 +35,10 @@ import lombok.core.HandlerPriority; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.ExtensionMethod; +import lombok.spi.Provides; // This handler just does some additional error checking; the real work is done in the agent. -@ProviderFor(EclipseAnnotationHandler.class) +@Provides @HandlerPriority(66560) // 2^16 + 2^10; we must run AFTER HandleVal which is at 2^16 public class HandleExtensionMethod extends EclipseAnnotationHandler<ExtensionMethod> { @Override public void handle(AnnotationValues<ExtensionMethod> annotation, Annotation ast, EclipseNode annotationNode) { diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java index 4927a8fb..5900e7ed 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2016 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,6 +38,7 @@ import lombok.eclipse.EclipseNode; import lombok.experimental.FieldDefaults; import lombok.experimental.NonFinal; import lombok.experimental.PackagePrivate; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -47,12 +48,11 @@ 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.mangosdk.spi.ProviderFor; /** * Handles the {@code lombok.FieldDefaults} annotation for eclipse. */ -@ProviderFor(EclipseASTVisitor.class) +@Provides(EclipseASTVisitor.class) @HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier. public class HandleFieldDefaults extends EclipseASTAdapter { public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) { diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java index cee3912c..cc1a5c3f 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2019 The Project Lombok Authors. + * Copyright (C) 2014-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,7 +41,6 @@ import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; -import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; import lombok.ConfigurationKeys; @@ -54,8 +53,9 @@ import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; import lombok.experimental.FieldNameConstants; +import lombok.spi.Provides; -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldNameConstants> { private static final IdentifierName FIELDS = IdentifierName.valueOf("Fields"); diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java index 755311b1..7f8fdef2 100644 --- a/src/core/lombok/eclipse/handlers/HandleGetter.java +++ b/src/core/lombok/eclipse/handlers/HandleGetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,6 +35,7 @@ import java.util.Map; import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.experimental.Delegate; +import lombok.spi.Provides; import lombok.Getter; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; @@ -73,14 +74,14 @@ 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.lookup.TypeConstants; -import org.mangosdk.spi.ProviderFor; /** * Handles the {@code lombok.Getter} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleGetter extends EclipseAnnotationHandler<Getter> { private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0]; + private static final String GETTER_NODE_NOT_SUPPORTED_ERR = "@Getter is only supported on a class, an enum, or a field."; public boolean generateGetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelGetter, List<Annotation> onMethod) { if (checkForTypeLevelGetter) { @@ -91,7 +92,7 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> { } if (!isClassOrEnum(typeNode)) { - pos.addError("@Getter is only supported on a class, an enum, or a field."); + pos.addError(GETTER_NODE_NOT_SUPPORTED_ERR); return false; } @@ -165,8 +166,9 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> { public void createGetterForField(AccessLevel level, EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists, boolean lazy, List<Annotation> onMethod) { + if (fieldNode.getKind() != Kind.FIELD) { - errorNode.addError("@Getter is only supported on a class or a field."); + errorNode.addError(GETTER_NODE_NOT_SUPPORTED_ERR); return; } @@ -290,6 +292,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/HandleHelper.java b/src/core/lombok/eclipse/handlers/HandleHelper.java index 36f53813..7977c507 100755 --- a/src/core/lombok/eclipse/handlers/HandleHelper.java +++ b/src/core/lombok/eclipse/handlers/HandleHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2016 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -45,7 +45,6 @@ import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; -import org.mangosdk.spi.ProviderFor; import lombok.ConfigurationKeys; import lombok.core.AST.Kind; @@ -53,11 +52,12 @@ import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.Helper; +import lombok.spi.Provides; /** * Handles the {@code lombok.Cleanup} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleHelper extends EclipseAnnotationHandler<Helper> { private Statement[] getStatementsFromAstNode(ASTNode node) { if (node instanceof Block) return ((Block) node).statements; diff --git a/src/core/lombok/eclipse/handlers/HandleJacksonized.java b/src/core/lombok/eclipse/handlers/HandleJacksonized.java index 90ee7582..466cc338 100644 --- a/src/core/lombok/eclipse/handlers/HandleJacksonized.java +++ b/src/core/lombok/eclipse/handlers/HandleJacksonized.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2020 The Project Lombok Authors. + * Copyright (C) 2013-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,7 +37,6 @@ import org.eclipse.jdt.internal.compiler.ast.StringLiteral; 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.mangosdk.spi.ProviderFor; import lombok.Builder; import lombok.ConfigurationKeys; @@ -50,13 +49,15 @@ import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.SuperBuilder; import lombok.extern.jackson.Jacksonized; +import lombok.spi.Provides; /** * This (ecj) handler deals with {@code @Jacksonized} modifying the (already * generated) {@code @Builder} or {@code @SuperBuilder} to conform to Jackson's * needs for builders. */ -@ProviderFor(EclipseAnnotationHandler.class) @HandlerPriority(-512) // Above Handle(Super)Builder's level (builders must be already generated). +@Provides +@HandlerPriority(-512) // Above Handle(Super)Builder's level (builders must be already generated). public class HandleJacksonized extends EclipseAnnotationHandler<Jacksonized> { private static final char[][] JSON_POJO_BUILDER_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder"); diff --git a/src/core/lombok/eclipse/handlers/HandleLog.java b/src/core/lombok/eclipse/handlers/HandleLog.java index 9eae601b..df3989a4 100644 --- a/src/core/lombok/eclipse/handlers/HandleLog.java +++ b/src/core/lombok/eclipse/handlers/HandleLog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2019 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -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,13 +33,11 @@ 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; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.mangosdk.spi.ProviderFor; import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; @@ -52,10 +48,11 @@ import lombok.core.handlers.LoggingFramework; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; +import lombok.spi.Provides; public class HandleLog { private static final IdentifierName LOG = IdentifierName.valueOf("log"); - + private HandleLog() { throw new UnsupportedOperationException(); } @@ -85,14 +82,15 @@ public class HandleLog { annotationNode.addWarning("Field '" + logFieldName + "' already exists."); return; } + if (isRecord(owner) && !useStatic) { annotationNode.addError("Logger fields must be static in records."); return; } - + Object valueGuess = annotation.getValueGuess("topic"); Expression loggerTopic = (Expression) annotation.getActualExpression("topic"); - + if (valueGuess instanceof String && ((String) valueGuess).trim().isEmpty()) loggerTopic = null; if (framework.getDeclaration().getParametersWithTopic() == null && loggerTopic != null) { annotationNode.addError(framework.getAnnotationAsString() + " does not allow a topic."); @@ -160,19 +158,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; @@ -223,7 +208,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.apachecommons.CommonsLog} annotation for Eclipse. */ - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleCommonsLog extends EclipseAnnotationHandler<lombok.extern.apachecommons.CommonsLog> { @Override public void handle(AnnotationValues<lombok.extern.apachecommons.CommonsLog> annotation, Annotation source, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_COMMONS_FLAG_USAGE, "@apachecommons.CommonsLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -234,7 +219,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.java.Log} annotation for Eclipse. */ - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleJulLog extends EclipseAnnotationHandler<lombok.extern.java.Log> { @Override public void handle(AnnotationValues<lombok.extern.java.Log> annotation, Annotation source, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_JUL_FLAG_USAGE, "@java.Log", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -245,7 +230,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.log4j.Log4j} annotation for Eclipse. */ - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleLog4jLog extends EclipseAnnotationHandler<lombok.extern.log4j.Log4j> { @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j> annotation, Annotation source, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_LOG4J_FLAG_USAGE, "@Log4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -256,7 +241,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.log4j.Log4j2} annotation for Eclipse. */ - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleLog4j2Log extends EclipseAnnotationHandler<lombok.extern.log4j.Log4j2> { @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j2> annotation, Annotation source, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_LOG4J2_FLAG_USAGE, "@Log4j2", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -267,7 +252,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.slf4j.Slf4j} annotation for Eclipse. */ - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleSlf4jLog extends EclipseAnnotationHandler<lombok.extern.slf4j.Slf4j> { @Override public void handle(AnnotationValues<lombok.extern.slf4j.Slf4j> annotation, Annotation source, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_SLF4J_FLAG_USAGE, "@Slf4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -278,7 +263,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.slf4j.XSlf4j} annotation for Eclipse. */ - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleXSlf4jLog extends EclipseAnnotationHandler<lombok.extern.slf4j.XSlf4j> { @Override public void handle(AnnotationValues<lombok.extern.slf4j.XSlf4j> annotation, Annotation source, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_XSLF4J_FLAG_USAGE, "@XSlf4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -289,7 +274,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.jbosslog.JBossLog} annotation for Eclipse. */ - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleJBossLog extends EclipseAnnotationHandler<lombok.extern.jbosslog.JBossLog> { @Override public void handle(AnnotationValues<lombok.extern.jbosslog.JBossLog> annotation, Annotation source, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_JBOSSLOG_FLAG_USAGE, "@JBossLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -300,7 +285,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.flogger.Flogger} annotation for Eclipse. */ - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleFloggerLog extends EclipseAnnotationHandler<lombok.extern.flogger.Flogger> { @Override public void handle(AnnotationValues<lombok.extern.flogger.Flogger> annotation, Annotation source, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_FLOGGER_FLAG_USAGE, "@Flogger", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -311,7 +296,7 @@ public class HandleLog { /** * Handles the {@link lombok.CustomLog} annotation for Eclipse. */ - @ProviderFor(EclipseAnnotationHandler.class) + @Provides public static class HandleCustomLog extends EclipseAnnotationHandler<lombok.CustomLog> { @Override public void handle(AnnotationValues<lombok.CustomLog> annotation, Annotation source, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_CUSTOM_FLAG_USAGE, "@CustomLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); diff --git a/src/core/lombok/eclipse/handlers/HandleNonNull.java b/src/core/lombok/eclipse/handlers/HandleNonNull.java index 8dc4f4f7..27e78d32 100644 --- a/src/core/lombok/eclipse/handlers/HandleNonNull.java +++ b/src/core/lombok/eclipse/handlers/HandleNonNull.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2019 The Project Lombok Authors. + * Copyright (C) 2013-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -47,7 +47,6 @@ import org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement; import org.eclipse.jdt.internal.compiler.ast.ThrowStatement; import org.eclipse.jdt.internal.compiler.ast.TryStatement; import org.eclipse.jdt.internal.compiler.ast.TypeReference; -import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; import lombok.ConfigurationKeys; @@ -55,13 +54,14 @@ import lombok.NonNull; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; +import lombok.eclipse.EcjAugments; import lombok.eclipse.EclipseAST; import lombok.eclipse.EclipseAnnotationHandler; -import lombok.eclipse.EclipseAugments; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists; +import lombok.spi.Provides; -@ProviderFor(EclipseAnnotationHandler.class) +@Provides @HandlerPriority(value = 512) // 2^9; onParameter=@__(@NonNull) has to run first. public class HandleNonNull extends EclipseAnnotationHandler<NonNull> { private static final char[] REQUIRE_NON_NULL = "requireNonNull".toCharArray(); @@ -91,7 +91,7 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> { if (isRecordField(annotationNode.up()) && !lombokConstructorExists(typeNode)) { handleConstructor.generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, null, SkipIfConstructorExists.NO, Collections.<Annotation>emptyList(), annotationNode); } - EclipseAugments.ASTNode_handled.clear(ast); + EcjAugments.ASTNode_handled.clear(ast); return; } diff --git a/src/core/lombok/eclipse/handlers/HandlePrintAST.java b/src/core/lombok/eclipse/handlers/HandlePrintAST.java index 234e29b8..9cc3e1ae 100644 --- a/src/core/lombok/eclipse/handlers/HandlePrintAST.java +++ b/src/core/lombok/eclipse/handlers/HandlePrintAST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,6 @@ import java.io.FileNotFoundException; import java.io.PrintStream; import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.mangosdk.spi.ProviderFor; import lombok.Lombok; import lombok.core.AnnotationValues; @@ -36,11 +35,12 @@ import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseASTVisitor; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.spi.Provides; /** * Handles the {@code lombok.core.PrintAST} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides @DeferUntilPostDiet @HandlerPriority(536870912) // 2^29; this handler is customarily run at the very end. public class HandlePrintAST extends EclipseAnnotationHandler<PrintAST> { diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index 9ebbde6d..ddae21fb 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2019 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,6 +38,7 @@ import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -56,13 +57,14 @@ 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.lookup.TypeIds; -import org.mangosdk.spi.ProviderFor; /** * Handles the {@code lombok.Setter} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleSetter extends EclipseAnnotationHandler<Setter> { + private static final String SETTER_NODE_NOT_SUPPORTED_ERR = "@Setter is only supported on a class or a field."; + public boolean generateSetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelSetter, List<Annotation> onMethod, List<Annotation> onParam) { if (checkForTypeLevelSetter) { if (hasAnnotation(Setter.class, typeNode)) { @@ -72,7 +74,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { } if (!isClass(typeNode)) { - pos.addError("@Setter is only supported on a class or a field."); + pos.addError(SETTER_NODE_NOT_SUPPORTED_ERR); return false; } @@ -142,7 +144,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { ASTNode source = sourceNode.get(); if (fieldNode.getKind() != Kind.FIELD) { - sourceNode.addError("@Setter is only supported on a class or a field."); + sourceNode.addError(SETTER_NODE_NOT_SUPPORTED_ERR); return; } @@ -257,6 +259,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/HandleSneakyThrows.java b/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java index 481dbcde..5f0ca3fb 100644 --- a/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java +++ b/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,7 @@ import lombok.core.HandlerPriority; import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -56,12 +57,11 @@ import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.ThrowStatement; import org.eclipse.jdt.internal.compiler.ast.TryStatement; import org.eclipse.jdt.internal.compiler.ast.TypeReference; -import org.mangosdk.spi.ProviderFor; /** * Handles the {@code lombok.HandleSneakyThrows} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides @DeferUntilPostDiet @HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleSneakyThrows extends EclipseAnnotationHandler<SneakyThrows> { diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 89c27fe3..4f4baecd 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2020 The Project Lombok Authors. + * Copyright (C) 2013-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,6 +21,7 @@ */ package lombok.eclipse.handlers; +import static lombok.eclipse.handlers.HandleBuilder.*; import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; @@ -49,6 +50,7 @@ import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.IfStatement; +import org.eclipse.jdt.internal.compiler.ast.Initializer; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; @@ -57,13 +59,10 @@ import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; -import org.eclipse.jdt.internal.compiler.ast.Receiver; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; -import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.Statement; -import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.SuperReference; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; @@ -76,7 +75,6 @@ import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; -import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; import lombok.Builder; @@ -99,75 +97,102 @@ import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker; import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker; import lombok.eclipse.handlers.HandleBuilder.BuilderFieldData; +import lombok.eclipse.handlers.HandleBuilder.BuilderJob; import lombok.experimental.NonFinal; import lombok.experimental.SuperBuilder; +import lombok.spi.Provides; -@ProviderFor(EclipseAnnotationHandler.class) +@Provides @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes. public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { - private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray(); - private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray(); - private static final char[] DEFAULT_PREFIX = "$default$".toCharArray(); - private static final char[] SET_PREFIX = "$set".toCharArray(); - private static final char[] VALUE_PREFIX = "$value".toCharArray(); private static final char[] SELF_METHOD_NAME = "self".toCharArray(); - private static final String TO_BUILDER_METHOD_NAME_STRING = "toBuilder"; - private static final char[] TO_BUILDER_METHOD_NAME = TO_BUILDER_METHOD_NAME_STRING.toCharArray(); private static final char[] FILL_VALUES_METHOD_NAME = "$fillValuesFrom".toCharArray(); private static final char[] FILL_VALUES_STATIC_METHOD_NAME = "$fillValuesFromInstanceIntoBuilder".toCharArray(); private static final char[] INSTANCE_VARIABLE_NAME = "instance".toCharArray(); private static final String BUILDER_VARIABLE_NAME_STRING = "b"; private static final char[] BUILDER_VARIABLE_NAME = BUILDER_VARIABLE_NAME_STRING.toCharArray(); - private static final AbstractMethodDeclaration[] EMPTY_METHODS = {}; - - @Override - public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, EclipseNode annotationNode) { - handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); - CheckerFrameworkVersion cfv = getCheckerFrameworkVersion(annotationNode); - - long p = (long) ast.sourceStart << 32 | ast.sourceEnd; + class SuperBuilderJob extends BuilderJob { + void init(AnnotationValues<SuperBuilder> annValues, SuperBuilder ann, EclipseNode node) { + accessOuters = accessInners = AccessLevel.PUBLIC; + oldFluent = true; + oldChain = true; + + builderMethodName = ann.builderMethodName(); + buildMethodName = ann.buildMethodName(); + toBuilder = ann.toBuilder(); + + if (builderMethodName == null) builderMethodName = "builder"; + if (buildMethodName == null) buildMethodName = "build"; + builderClassName = fixBuilderClassName(node, ""); + } - SuperBuilder superbuilderAnnotation = annotation.getInstance(); + EclipseNode builderAbstractType; + String builderAbstractClassName; + char[] builderAbstractClassNameArr; + EclipseNode builderImplType; + String builderImplClassName; + char[] builderImplClassNameArr; + private TypeParameter[] builderTypeParams_; + void setBuilderToImpl() { + builderType = builderImplType; + builderClassName = builderImplClassName; + builderClassNameArr = builderImplClassNameArr; + builderTypeParams = typeParams; + } - String builderMethodName = superbuilderAnnotation.builderMethodName(); - String buildMethodName = superbuilderAnnotation.buildMethodName(); + void setBuilderToAbstract() { + builderType = builderAbstractType; + builderClassName = builderAbstractClassName; + builderClassNameArr = builderAbstractClassNameArr; + builderTypeParams = builderTypeParams_; + } + } + + @Override public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, EclipseNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); + SuperBuilderJob job = new SuperBuilderJob(); + job.sourceNode = annotationNode; + job.source = ast; + job.checkerFramework = getCheckerFrameworkVersion(annotationNode); + job.isStatic = true; - if (builderMethodName == null) builderMethodName = "builder"; - if (buildMethodName == null) buildMethodName = "build"; + SuperBuilder annInstance = annotation.getInstance(); + job.init(annotation, annInstance, annotationNode); boolean generateBuilderMethod; - if (builderMethodName.isEmpty()) { + if (job.builderMethodName.isEmpty()) { generateBuilderMethod = false; - } else if (!checkName("builderMethodName", builderMethodName, annotationNode)) { + } else if (!checkName("builderMethodName", job.builderMethodName, annotationNode)) { return; } else { generateBuilderMethod = true; } - if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; - boolean toBuilder = superbuilderAnnotation.toBuilder(); + if (!checkName("buildMethodName", job.buildMethodName, annotationNode)) return; - EclipseNode tdParent = annotationNode.up(); + EclipseNode parent = annotationNode.up(); - java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>(); - TypeReference returnType; - TypeParameter[] typeParams; + job.builderFields = new ArrayList<BuilderFieldData>(); + TypeReference buildMethodReturnType; boolean addCleaning = false; - if (!isClass(tdParent)) { - annotationNode.addError("@SuperBuilder is only supported on types."); + List<EclipseNode> nonFinalNonDefaultedFields = null; + + if (!isClass(parent)) { + annotationNode.addError("@SuperBuilder is only supported on classes."); return; } - TypeDeclaration td = (TypeDeclaration) tdParent.get(); + + job.parentType = parent; + TypeDeclaration td = (TypeDeclaration) parent.get(); // Gather all fields of the class that should be set by the builder. List<EclipseNode> allFields = new ArrayList<EclipseNode>(); - List<EclipseNode> nonFinalNonDefaultedFields = null; - boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent)); - for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { + boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); + for (EclipseNode fieldNode : HandleConstructor.findAllFields(parent, true)) { FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode); boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); @@ -180,7 +205,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { bfd.builderFieldName = bfd.name; bfd.annotations = copyAnnotations(fd, copyableAnnotations); bfd.type = fd.type; - bfd.singularData = getSingularData(fieldNode, ast, superbuilderAnnotation.setterPrefix()); + bfd.singularData = getSingularData(fieldNode, ast, annInstance.setterPrefix()); bfd.originalFieldNode = fieldNode; if (bfd.singularData != null && isDefault != null) { @@ -205,21 +230,15 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { bfd.builderFieldName = prefixWith(bfd.name, VALUE_PREFIX); MethodDeclaration md = HandleBuilder.generateDefaultProvider(bfd.nameOfDefaultProvider, td.typeParameters, fieldNode, ast); - if (md != null) injectMethod(tdParent, md); + if (md != null) injectMethod(parent, md); } addObtainVia(bfd, fieldNode); - builderFields.add(bfd); + job.builderFields.add(bfd); allFields.add(fieldNode); } - // Set the names of the builder classes. - String builderClassNameTemplate = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME); - if (builderClassNameTemplate == null || builderClassNameTemplate.isEmpty()) builderClassNameTemplate = "*Builder"; - String builderClassName = builderClassNameTemplate.replace("*", String.valueOf(td.name)); - String builderImplClassName = builderClassName + "Impl"; - - typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0]; - returnType = namePlusTypeParamsToTypeReference(tdParent, typeParams, p); + job.typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0]; + buildMethodReturnType = job.createBuilderParentTypeReference(); // <C, B> are the generics for our builder. String classGenericName = "C"; @@ -227,25 +246,41 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { // We have to make sure that the generics' names do not collide with any generics on the annotated class, // the classname itself, or any member type name of the annotated class. // For instance, if there are generics <B, B2, C> on the annotated class, use "C2" and "B3" for our builder. - java.util.Set<String> usedNames = gatherUsedTypeNames(typeParams, td); + java.util.Set<String> usedNames = gatherUsedTypeNames(job.typeParams, td); classGenericName = generateNonclashingNameFor(classGenericName, usedNames); builderGenericName = generateNonclashingNameFor(builderGenericName, usedNames); + TypeParameter[] paddedTypeParameters; { + paddedTypeParameters = new TypeParameter[job.typeParams.length + 2]; + System.arraycopy(job.typeParams, 0, paddedTypeParameters, 0, job.typeParams.length); + + TypeParameter c = new TypeParameter(); + c.name = classGenericName.toCharArray(); + c.type = cloneSelfType(job.parentType, job.source); + paddedTypeParameters[paddedTypeParameters.length - 2] = c; + + TypeParameter b = new TypeParameter(); + b.name = builderGenericName.toCharArray(); + b.type = cloneSelfType(job.parentType, job.source); + paddedTypeParameters[paddedTypeParameters.length - 1] = b; + } + job.builderTypeParams = job.builderTypeParams_ = paddedTypeParameters; + TypeReference extendsClause = td.superclass; TypeReference superclassBuilderClass = null; TypeReference[] typeArguments = new TypeReference[] { new SingleTypeReference(classGenericName.toCharArray(), 0), - new SingleTypeReference(builderGenericName.toCharArray(), 0) + new SingleTypeReference(builderGenericName.toCharArray(), 0), }; if (extendsClause instanceof QualifiedTypeReference) { QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference)extendsClause; String superclassClassName = String.valueOf(qualifiedTypeReference.getLastToken()); - String superclassBuilderClassName = builderClassNameTemplate.replace("*", superclassClassName); + String superclassBuilderClassName = job.replaceBuilderClassName(superclassClassName); char[][] tokens = Arrays.copyOf(qualifiedTypeReference.tokens, qualifiedTypeReference.tokens.length + 1); tokens[tokens.length-1] = superclassBuilderClassName.toCharArray(); long[] poss = new long[tokens.length]; - Arrays.fill(poss, p); + Arrays.fill(poss, job.getPos()); TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause); @@ -261,7 +296,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { char[][] tokens = new char[][] {superClass.toCharArray(), superclassBuilderClassName.toCharArray()}; long[] poss = new long[tokens.length]; - Arrays.fill(poss, p); + Arrays.fill(poss, job.getPos()); TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause); @@ -272,40 +307,43 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss); } + job.builderAbstractClassName = job.builderClassName = job.replaceBuilderClassName(td.name); + job.builderAbstractClassNameArr = job.builderClassNameArr = job.builderAbstractClassName.toCharArray(); + job.builderImplClassName = job.builderAbstractClassName + "Impl"; + job.builderImplClassNameArr = job.builderImplClassName.toCharArray(); + // If there is no superclass, superclassBuilderClassExpression is still == null at this point. // You can use it to check whether to inherit or not. - if (!constructorExists(tdParent, builderClassName)) { - generateBuilderBasedConstructor(cfv, tdParent, typeParams, builderFields, annotationNode, builderClassName, - superclassBuilderClass != null); + if (!constructorExists(parent, job.builderClassName)) { + generateBuilderBasedConstructor(job, superclassBuilderClass != null); } // Create the abstract builder class, or reuse an existing one. - EclipseNode builderType = findInnerClass(tdParent, builderClassName); - if (builderType == null) { - builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass, - typeParams, ast, classGenericName, builderGenericName); + job.builderAbstractType = findInnerClass(parent, job.builderClassName); + if (job.builderAbstractType == null) { + job.builderAbstractType = generateBuilderAbstractClass(job, superclassBuilderClass, classGenericName, builderGenericName); } else { - TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get(); + TypeDeclaration builderTypeDeclaration = (TypeDeclaration) job.builderAbstractType.get(); if ((builderTypeDeclaration.modifiers & (ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract)) == 0) { annotationNode.addError("Existing Builder must be an abstract static inner class."); return; } - sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); + sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(job.builderAbstractType, annotationNode); // Generate errors for @Singular BFDs that have one already defined node. - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { SingularData sd = bfd.singularData; if (sd == null) continue; EclipseSingularizer singularizer = sd.getSingularizer(); if (singularizer == null) continue; - if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) { + if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderAbstractType, sd)) { bfd.singularData = null; } } } // Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary. - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { if (bfd.singularData.getSingularizer().requiresCleaning()) { addCleaning = true; @@ -325,47 +363,52 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } // Generate the fields in the abstract builder class that hold the values for the instance. - generateBuilderFields(builderType, builderFields, ast); + job.setBuilderToAbstract(); + generateBuilderFields(job); if (addCleaning) { FieldDeclaration cleanDecl = new FieldDeclaration(CLEAN_FIELD_NAME, 0, -1); cleanDecl.declarationSourceEnd = -1; cleanDecl.modifiers = ClassFileConstants.AccPrivate; cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); - injectFieldAndMarkGenerated(builderType, cleanDecl); + injectFieldAndMarkGenerated(job.builderType, cleanDecl); } - if (toBuilder) { + if (job.toBuilder) { // Generate $fillValuesFrom() method in the abstract builder. - injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, builderClassName, typeParams)); + injectMethod(job.builderType, generateFillValuesMethod(job, superclassBuilderClass != null, builderGenericName, classGenericName)); // Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class. - injectMethod(builderType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, ast, superbuilderAnnotation.setterPrefix())); + injectMethod(job.builderType, generateStaticFillValuesMethod(job, annInstance.setterPrefix())); } // Generate abstract self() and build() methods in the abstract builder. - injectMethod(builderType, generateAbstractSelfMethod(cfv, tdParent, superclassBuilderClass != null, builderGenericName)); - injectMethod(builderType, generateAbstractBuildMethod(cfv, builderType, buildMethodName, builderFields, superclassBuilderClass != null, classGenericName, ast)); + injectMethod(job.builderType, generateAbstractSelfMethod(job, superclassBuilderClass != null, builderGenericName)); + job.setBuilderToAbstract(); + injectMethod(job.builderType, generateAbstractBuildMethod(job, superclassBuilderClass != null, classGenericName)); // Create the setter methods in the abstract builder. - for (BuilderFieldData bfd : builderFields) { - generateSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, builderGenericName, superbuilderAnnotation.setterPrefix()); + for (BuilderFieldData bfd : job.builderFields) { + generateSetterMethodsForBuilder(job, bfd, builderGenericName, annInstance.setterPrefix()); } // Create the toString() method for the abstract builder. - if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { + if (methodExists("toString", job.builderType, 0) == MemberExistsResult.NOT_EXISTS) { List<Included<EclipseNode, ToString.Include>> fieldNodes = new ArrayList<Included<EclipseNode, ToString.Include>>(); - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { for (EclipseNode f : bfd.createdFields) { fieldNodes.add(new Included<EclipseNode, ToString.Include>(f, null, true, false)); } } // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder. - MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClass != null, ast, FieldAccess.ALWAYS_FIELD); + MethodDeclaration md = HandleToString.createToString(job.builderType, fieldNodes, true, superclassBuilderClass != null, ast, FieldAccess.ALWAYS_FIELD); if (md != null) { - injectMethod(builderType, md); + injectMethod(job.builderType, md); } } - if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); + if (addCleaning) { + job.setBuilderToAbstract(); + injectMethod(job.builderType, generateCleanMethod(job)); + } boolean isAbstract = (td.modifiers & ClassFileConstants.AccAbstract) != 0; if (isAbstract) { @@ -374,26 +417,27 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } // Create the builder implementation class, or reuse an existing one. - EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName); - if (builderImplType == null) { - builderImplType = generateBuilderImplClass(tdParent, builderImplClassName, builderClassName, typeParams, ast); + job.builderImplType = findInnerClass(parent, job.builderImplClassName); + if (job.builderImplType == null) { + job.builderImplType = generateBuilderImplClass(job, job.builderImplClassName); } else { - TypeDeclaration builderImplTypeDeclaration = (TypeDeclaration) builderImplType.get(); + TypeDeclaration builderImplTypeDeclaration = (TypeDeclaration) job.builderImplType.get(); if ((builderImplTypeDeclaration.modifiers & ClassFileConstants.AccAbstract) != 0 || (builderImplTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) { annotationNode.addError("Existing BuilderImpl must be a non-abstract static inner class."); return; } - sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderImplType, annotationNode); + sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(job.builderImplType, annotationNode); } - if (toBuilder) { + job.setBuilderToImpl(); + if (job.toBuilder) { // Add the toBuilder() method to the annotated class. - switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) { + switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, job.parentType, 0)) { case EXISTS_BY_USER: break; case NOT_EXISTS: - injectMethod(tdParent, generateToBuilderMethod(cfv, builderClassName, builderImplClassName, tdParent, typeParams, ast)); + injectMethod(parent, generateToBuilderMethod(job)); break; default: // Should not happen. @@ -401,17 +445,19 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } // Create the self() and build() methods in the BuilderImpl. - injectMethod(builderImplType, generateSelfMethod(cfv, builderImplType, typeParams, p)); + job.setBuilderToImpl(); + injectMethod(job.builderImplType, generateSelfMethod(job)); - if (methodExists(buildMethodName, builderImplType, -1) == MemberExistsResult.NOT_EXISTS) { - injectMethod(builderImplType, generateBuildMethod(cfv, builderImplType, buildMethodName, returnType, builderFields, ast)); + if (methodExists(job.buildMethodName, job.builderImplType, -1) == MemberExistsResult.NOT_EXISTS) { + job.setBuilderToImpl(); + injectMethod(job.builderImplType, generateBuildMethod(job, buildMethodReturnType)); } // Add the builder() method to the annotated class. - if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; + if (generateBuilderMethod && methodExists(job.builderMethodName, parent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; if (generateBuilderMethod) { - MethodDeclaration md = generateBuilderMethod(cfv, builderMethodName, builderClassName, builderImplClassName, tdParent, typeParams, ast); - if (md != null) injectMethod(tdParent, md); + MethodDeclaration md = generateBuilderMethod(job); + if (md != null) injectMethod(parent, md); } if (nonFinalNonDefaultedFields != null && generateBuilderMethod) { @@ -421,97 +467,82 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } } - private EclipseNode generateBuilderAbstractClass(EclipseNode tdParent, String builderClass, - TypeReference superclassBuilderClass, TypeParameter[] typeParams, - ASTNode source, String classGenericName, String builderGenericName) { - - TypeDeclaration parent = (TypeDeclaration) tdParent.get(); + private EclipseNode generateBuilderAbstractClass(BuilderJob job, TypeReference superclassBuilderClass, String classGenericName, String builderGenericName) { + TypeDeclaration parent = (TypeDeclaration) job.parentType.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; builder.modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract; - builder.name = builderClass.toCharArray(); + builder.name = job.builderClassNameArr; // Keep any type params of the annotated class. - builder.typeParameters = Arrays.copyOf(copyTypeParams(typeParams, source), typeParams.length + 2); + builder.typeParameters = Arrays.copyOf(copyTypeParams(job.typeParams, job.source), job.typeParams.length + 2); // Add builder-specific type params required for inheritable builders. // 1. The return type for the build() method, named "C", which extends the annotated class. TypeParameter o = new TypeParameter(); o.name = classGenericName.toCharArray(); - o.type = cloneSelfType(tdParent, source); + o.type = cloneSelfType(job.parentType, job.source); builder.typeParameters[builder.typeParameters.length - 2] = o; // 2. The return type for all setter methods, named "B", which extends this builder class. o = new TypeParameter(); o.name = builderGenericName.toCharArray(); - TypeReference[] typerefs = appendBuilderTypeReferences(typeParams, classGenericName, builderGenericName); - o.type = generateParameterizedTypeReference(tdParent, builderClass.toCharArray(), false, typerefs, 0); + TypeReference[] typerefs = appendBuilderTypeReferences(job.typeParams, classGenericName, builderGenericName); + o.type = generateParameterizedTypeReference(job.parentType, job.builderClassNameArr, false, typerefs, 0); builder.typeParameters[builder.typeParameters.length - 1] = o; - builder.superclass = copyType(superclassBuilderClass, source); + if (superclassBuilderClass != null) builder.superclass = copyType(superclassBuilderClass, job.source); builder.createDefaultConstructor(false, true); - builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); - return injectType(tdParent, builder); + builder.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null); + return injectType(job.parentType, builder); } - private EclipseNode generateBuilderImplClass(EclipseNode tdParent, String builderImplClass, String builderAbstractClass, TypeParameter[] typeParams, ASTNode source) { - TypeDeclaration parent = (TypeDeclaration) tdParent.get(); + private EclipseNode generateBuilderImplClass(BuilderJob job, String builderImplClass) { + TypeDeclaration parent = (TypeDeclaration) job.parentType.get(); TypeDeclaration builder = new TypeDeclaration(parent.compilationResult); builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; builder.modifiers |= ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal; builder.name = builderImplClass.toCharArray(); // Add type params if there are any. - if (typeParams != null && typeParams.length > 0) builder.typeParameters = copyTypeParams(typeParams, source); + if (job.typeParams != null && job.typeParams.length > 0) builder.typeParameters = copyTypeParams(job.typeParams, job.source); - if (builderAbstractClass != null) { + if (job.builderClassName != null) { // Extend the abstract builder. // 1. Add any type params of the annotated class. - TypeReference[] typeArgs = new TypeReference[typeParams.length + 2]; - for (int i = 0; i < typeParams.length; i++) { - typeArgs[i] = new SingleTypeReference(typeParams[i].name, 0); + TypeReference[] typeArgs = new TypeReference[job.typeParams.length + 2]; + for (int i = 0; i < job.typeParams.length; i++) { + typeArgs[i] = new SingleTypeReference(job.typeParams[i].name, 0); } // 2. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. // 3. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. - typeArgs[typeArgs.length - 2] = cloneSelfType(tdParent, source); - typeArgs[typeArgs.length - 1] = createTypeReferenceWithTypeParameters(tdParent, builderImplClass, typeParams); - builder.superclass = generateParameterizedTypeReference(tdParent, builderAbstractClass.toCharArray(), false, typeArgs, 0); + typeArgs[typeArgs.length - 2] = cloneSelfType(job.parentType, job.source); + typeArgs[typeArgs.length - 1] = createTypeReferenceWithTypeParameters(job.parentType, builderImplClass, job.typeParams); + builder.superclass = generateParameterizedTypeReference(job.parentType, job.builderClassNameArr, false, typeArgs, 0); } builder.createDefaultConstructor(false, true); - builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); - return injectType(tdParent, builder); + builder.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null); + return injectType(job.parentType, builder); } /** * Generates a constructor that has a builder as the only parameter. * The values from the builder are used to initialize the fields of new instances. * - * @param typeNode - * the type (with the {@code @Builder} annotation) for which a - * constructor should be generated. - * @param typeParams - * @param cfv Settings for generating checker framework annotations - * @param builderFields a list of fields in the builder which should be assigned to new instances. - * @param source the annotation (used for setting source code locations for the generated code). * @param callBuilderBasedSuperConstructor * If {@code true}, the constructor will explicitly call a super * constructor with the builder as argument. Requires * {@code builderClassAsParameter != null}. */ - private void generateBuilderBasedConstructor(CheckerFrameworkVersion cfv, EclipseNode typeNode, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, - EclipseNode sourceNode, String builderClassName, boolean callBuilderBasedSuperConstructor) { + private void generateBuilderBasedConstructor(BuilderJob job, boolean callBuilderBasedSuperConstructor) { + TypeDeclaration typeDeclaration = ((TypeDeclaration) job.parentType.get()); + long p = job.getPos(); - ASTNode source = sourceNode.get(); - - TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get()); - long p = (long) source.sourceStart << 32 | source.sourceEnd; - - ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); + ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) job.parentType.top().get()).compilationResult); constructor.modifiers = toEclipseModifier(AccessLevel.PROTECTED); - if (cfv.generateUnique()) constructor.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)}; constructor.selector = typeDeclaration.name; if (callBuilderBasedSuperConstructor) { constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); @@ -519,21 +550,21 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } else { constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); } - constructor.constructorCall.sourceStart = source.sourceStart; - constructor.constructorCall.sourceEnd = source.sourceEnd; + constructor.constructorCall.sourceStart = job.source.sourceStart; + constructor.constructorCall.sourceEnd = job.source.sourceEnd; constructor.thrownExceptions = null; constructor.typeParameters = null; constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart; - constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; + constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = job.source.sourceStart; + constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = job.source.sourceEnd; TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)}; - TypeReference builderType = generateParameterizedTypeReference(typeNode, builderClassName.toCharArray(), false, mergeToTypeReferences(typeParams, wildcards), p); + TypeReference builderType = generateParameterizedTypeReference(job.parentType, job.builderClassNameArr, false, mergeToTypeReferences(job.typeParams, wildcards), p); constructor.arguments = new Argument[] {new Argument(BUILDER_VARIABLE_NAME, p, builderType, Modifier.FINAL)}; List<Statement> statements = new ArrayList<Statement>(); - for (BuilderFieldData fieldNode : builderFields) { + for (BuilderFieldData fieldNode : job.builderFields) { FieldReference fieldInThis = new FieldReference(fieldNode.rawName, p); int s = (int) (p >> 32); int e = (int) p; @@ -541,7 +572,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { Expression assignmentExpr; if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) { - fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.builderFieldName, BUILDER_VARIABLE_NAME_STRING); + fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, job.parentType, statements, fieldNode.builderFieldName, BUILDER_VARIABLE_NAME_STRING); assignmentExpr = new SingleNameReference(fieldNode.builderFieldName, p); } else { char[][] variableInBuilder = new char[][] {BUILDER_VARIABLE_NAME, fieldNode.builderFieldName}; @@ -557,11 +588,11 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { QualifiedNameReference setVariableInBuilderRef = new QualifiedNameReference(setVariableInBuilder, positions, s, e); MessageSend defaultMethodCall = new MessageSend(); - defaultMethodCall.sourceStart = source.sourceStart; - defaultMethodCall.sourceEnd = source.sourceEnd; - defaultMethodCall.receiver = generateNameReference(typeNode, 0L); + defaultMethodCall.sourceStart = job.source.sourceStart; + defaultMethodCall.sourceEnd = job.source.sourceEnd; + defaultMethodCall.receiver = generateNameReference(job.parentType, 0L); defaultMethodCall.selector = fieldNode.nameOfDefaultProvider; - defaultMethodCall.typeArguments = typeParameterNames(((TypeDeclaration) typeNode.get()).typeParameters); + defaultMethodCall.typeArguments = typeParameterNames(((TypeDeclaration) job.parentType.get()).typeParameters); Statement defaultAssignment = new Assignment(fieldInThis, defaultMethodCall, (int) p); IfStatement ifBlockForDefault = new IfStatement(setVariableInBuilderRef, assignment, defaultAssignment, s, e); @@ -571,41 +602,50 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } if (hasNonNullAnnotations(fieldNode.originalFieldNode)) { - Statement nullCheck = generateNullCheck((FieldDeclaration) fieldNode.originalFieldNode.get(), sourceNode, null); + Statement nullCheck = generateNullCheck((FieldDeclaration) fieldNode.originalFieldNode.get(), job.sourceNode, null); if (nullCheck != null) statements.add(nullCheck); } } constructor.statements = statements.isEmpty() ? null : statements.toArray(new Statement[0]); + if (job.checkerFramework.generateSideEffectFree()) { + constructor.annotations = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE)}; + } - constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); + constructor.traverse(new SetGeneratedByVisitor(job.source), typeDeclaration.scope); - injectMethod(typeNode, constructor); + injectMethod(job.parentType, constructor); } - private MethodDeclaration generateBuilderMethod(CheckerFrameworkVersion cfv, String builderMethodName, String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { - int pS = source.sourceStart, pE = source.sourceEnd; + private MethodDeclaration generateBuilderMethod(SuperBuilderJob job) { + int pS = job.source.sourceStart, pE = job.source.sourceEnd; long p = (long) pS << 32 | pE; - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); - out.selector = builderMethodName.toCharArray(); + MethodDeclaration out = job.createNewMethodDeclaration(); + out.selector = job.builderMethodName.toCharArray(); out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; // Add type params if there are any. - if (typeParams != null && typeParams.length > 0) out.typeParameters = copyTypeParams(typeParams, source); + if (job.typeParams != null && job.typeParams.length > 0) out.typeParameters = copyTypeParams(job.typeParams, job.source); TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND) }; - out.returnType = generateParameterizedTypeReference(type, builderClassName.toCharArray(), false, mergeToTypeReferences(typeParams, wildcards), p); + out.returnType = generateParameterizedTypeReference(job.parentType, job.builderAbstractClassNameArr, false, mergeToTypeReferences(job.typeParams, wildcards), p); + if (job.checkerFramework.generateUnique()) { + int len = out.returnType.getTypeName().length; + out.returnType.annotations = new Annotation[len][]; + out.returnType.annotations[len - 1] = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__UNIQUE)}; + } AllocationExpression invoke = new AllocationExpression(); - invoke.type = namePlusTypeParamsToTypeReference(type, builderImplClassName.toCharArray(), false, typeParams, p); + invoke.type = namePlusTypeParamsToTypeReference(job.parentType, job.builderImplClassNameArr, false, job.typeParams, p); out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)}; + if (job.checkerFramework.generateSideEffectFree()) { + out.annotations = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE)}; + } - if (cfv.generateUnique()) out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)}; - - createRelevantNonNullAnnotation(type, out); - out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); + createRelevantNonNullAnnotation(job.parentType, out); + out.traverse(new SetGeneratedByVisitor(job.source), ((TypeDeclaration) job.parentType.get()).scope); return out; } @@ -617,30 +657,36 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { * } * </pre> */ - private MethodDeclaration generateToBuilderMethod(CheckerFrameworkVersion cfv, String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { - int pS = source.sourceStart, pE = source.sourceEnd; + private MethodDeclaration generateToBuilderMethod(SuperBuilderJob job) { + int pS = job.source.sourceStart, pE = job.source.sourceEnd; long p = (long) pS << 32 | pE; - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); + MethodDeclaration out = job.createNewMethodDeclaration(); out.selector = TO_BUILDER_METHOD_NAME; out.modifiers = ClassFileConstants.AccPublic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND) }; - out.returnType = generateParameterizedTypeReference(type, builderClassName.toCharArray(), false, mergeToTypeReferences(typeParams, wildcards), p); + out.returnType = generateParameterizedTypeReference(job.parentType, job.builderAbstractClassNameArr, false, mergeToTypeReferences(job.typeParams, wildcards), p); + if (job.checkerFramework.generateUnique()) { + int len = out.returnType.getTypeName().length; + out.returnType.annotations = new Annotation[len][]; + out.returnType.annotations[len - 1] = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__UNIQUE)}; + } AllocationExpression newClass = new AllocationExpression(); - newClass.type = namePlusTypeParamsToTypeReference(type, builderImplClassName.toCharArray(), false, typeParams, p); + newClass.type = namePlusTypeParamsToTypeReference(job.parentType, job.builderImplClassNameArr, false, job.typeParams, p); MessageSend invokeFillMethod = new MessageSend(); invokeFillMethod.receiver = newClass; invokeFillMethod.selector = FILL_VALUES_METHOD_NAME; invokeFillMethod.arguments = new Expression[] {new ThisReference(0, 0)}; out.statements = new Statement[] {new ReturnStatement(invokeFillMethod, pS, pE)}; + if (job.checkerFramework.generateSideEffectFree()) { + out.annotations = new Annotation[] {generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE)}; + } - if (cfv.generateUnique()) out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)}; - - createRelevantNonNullAnnotation(type, out); - out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); + createRelevantNonNullAnnotation(job.parentType, out); + out.traverse(new SetGeneratedByVisitor(job.source), ((TypeDeclaration) job.parentType.get()).scope); return out; } @@ -650,17 +696,17 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { * <pre> * protected B $fillValuesFrom(final C instance) { * super.$fillValuesFrom(instance); - * Foobar.FoobarBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this); + * Foobar.FoobarBuilder.$fillValuesFromInstanceIntoBuilder(instance, this); * return self(); * } * </pre> */ - private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, String builderClassName, TypeParameter[] typeParams) { - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); + private MethodDeclaration generateFillValuesMethod(SuperBuilderJob job, boolean inherited, String builderGenericName, String classGenericName) { + MethodDeclaration out = job.createNewMethodDeclaration(); out.selector = FILL_VALUES_METHOD_NAME; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.modifiers = ClassFileConstants.AccProtected; - if (inherited) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())}; + if (inherited) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, job.parentType.get())}; out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0); TypeReference builderType = new SingleTypeReference(classGenericName.toCharArray(), 0); @@ -679,7 +725,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { // Call the builder implemention's helper method that actually fills the values from the instance. MessageSend callStaticFillValuesMethod = new MessageSend(); - callStaticFillValuesMethod.receiver = generateNameReference(tdParent, builderClassName.toCharArray(), 0); + callStaticFillValuesMethod.receiver = generateNameReference(job.parentType, job.builderAbstractClassNameArr, 0); callStaticFillValuesMethod.selector = FILL_VALUES_STATIC_METHOD_NAME; callStaticFillValuesMethod.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0), new ThisReference(0, 0)}; body.add(callStaticFillValuesMethod); @@ -707,40 +753,40 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { * </pre> * @param setterPrefix the prefix for setter methods */ - private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source, String setterPrefix) { - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); + private MethodDeclaration generateStaticFillValuesMethod(BuilderJob job, String setterPrefix) { + MethodDeclaration out = job.createNewMethodDeclaration(); out.selector = FILL_VALUES_STATIC_METHOD_NAME; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic; out.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0); TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)}; - TypeReference builderType = generateParameterizedTypeReference(tdParent, builderClassName.toCharArray(), false, mergeToTypeReferences(typeParams, wildcards), 0); + TypeReference builderType = generateParameterizedTypeReference(job.parentType, job.builderClassNameArr, false, mergeToTypeReferences(job.typeParams, wildcards), 0); Argument builderArgument = new Argument(BUILDER_VARIABLE_NAME, 0, builderType, Modifier.FINAL); TypeReference[] typerefs = null; - if (typeParams.length > 0) { - typerefs = new TypeReference[typeParams.length]; - for (int i = 0; i < typeParams.length; i++) typerefs[i] = new SingleTypeReference(typeParams[i].name, 0); + if (job.typeParams.length > 0) { + typerefs = new TypeReference[job.typeParams.length]; + for (int i = 0; i < job.typeParams.length; i++) typerefs[i] = new SingleTypeReference(job.typeParams[i].name, 0); } - long p = source.sourceStart; - p = (p << 32) | source.sourceEnd; + long p = job.getPos(); - TypeReference parentArgument = typerefs == null ? generateTypeReference(tdParent, p) : generateParameterizedTypeReference(tdParent, typerefs, p); + TypeReference parentArgument = typerefs == null ? generateTypeReference(job.parentType, p) : generateParameterizedTypeReference(job.parentType, typerefs, p); out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, parentArgument, Modifier.FINAL), builderArgument}; // Add type params if there are any. - if (typeParams.length > 0) out.typeParameters = copyTypeParams(typeParams, source); + if (job.typeParams.length > 0) out.typeParameters = copyTypeParams(job.typeParams, job.source); List<Statement> body = new ArrayList<Statement>(); // Call the builder's setter methods to fill the values from the instance. - for (BuilderFieldData bfd : builderFields) { - MessageSend exec = createSetterCallWithInstanceValue(bfd, tdParent, source, setterPrefix); + for (BuilderFieldData bfd : job.builderFields) { + MessageSend exec = createSetterCallWithInstanceValue(bfd, job.parentType, job.source, setterPrefix); body.add(exec); } out.statements = body.isEmpty() ? null : body.toArray(new Statement[0]); + out.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null); return out; } @@ -772,9 +818,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { ms.arguments = tgt; } else { Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); - MessageSend emptyCollection = new MessageSend(); - emptyCollection.receiver = generateQualifiedNameRef(source, bfd.singularData.getSingularizer().getEmptyMakerReceiver(bfd.singularData.getTargetFqn())); - emptyCollection.selector = bfd.singularData.getSingularizer().getEmptyMakerSelector(bfd.singularData.getTargetFqn()); + MessageSend emptyCollection = bfd.singularData.getSingularizer().getEmptyExpression(bfd.singularData.getTargetFqn(), bfd.singularData, type, source); ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyCollection, tgt[1])}; } ms.receiver = new SingleNameReference(BUILDER_VARIABLE_NAME, 0); @@ -782,14 +826,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { return ms; } - private MethodDeclaration generateAbstractSelfMethod(CheckerFrameworkVersion cfv, EclipseNode tdParent, boolean override, String builderGenericName) { - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); + private MethodDeclaration generateAbstractSelfMethod(BuilderJob job, boolean override, String builderGenericName) { + MethodDeclaration out = job.createNewMethodDeclaration(); out.selector = SELF_METHOD_NAME; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected | ExtraCompilerModifiers.AccSemicolonBody; - Annotation overrideAnn = override ? makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get()) : null; - Annotation rrAnn = cfv.generateReturnsReceiver() ? generateNamedAnnotation(tdParent.get(), CheckerFrameworkVersion.NAME__RETURNS_RECEIVER): null; - Annotation sefAnn = cfv.generatePure() ? generateNamedAnnotation(tdParent.get(), CheckerFrameworkVersion.NAME__PURE): null; + Annotation overrideAnn = override ? makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, job.parentType.get()) : null; + Annotation rrAnn = job.checkerFramework.generateReturnsReceiver() ? generateNamedAnnotation(job.parentType.get(), CheckerFrameworkVersion.NAME__RETURNS_RECEIVER): null; + Annotation sefAnn = job.checkerFramework.generatePure() ? generateNamedAnnotation(job.parentType.get(), CheckerFrameworkVersion.NAME__PURE): null; if (overrideAnn != null && rrAnn != null && sefAnn != null) out.annotations = new Annotation[] {overrideAnn, rrAnn, sefAnn}; else if (overrideAnn != null && rrAnn != null) out.annotations = new Annotation[] {overrideAnn, rrAnn}; else if (overrideAnn != null && sefAnn != null) out.annotations = new Annotation[] {overrideAnn, sefAnn}; @@ -801,54 +845,52 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { return out; } - private MethodDeclaration generateSelfMethod(CheckerFrameworkVersion cfv, EclipseNode builderImplType, TypeParameter[] typeParams, long p) { - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderImplType.top().get()).compilationResult); + private MethodDeclaration generateSelfMethod(BuilderJob job) { + MethodDeclaration out = job.createNewMethodDeclaration(); out.selector = SELF_METHOD_NAME; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.modifiers = ClassFileConstants.AccProtected; - Annotation overrideAnn = makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, builderImplType.get()); - Annotation rrAnn = cfv.generateReturnsReceiver() ? generateNamedAnnotation(builderImplType.get(), CheckerFrameworkVersion.NAME__RETURNS_RECEIVER) : null; - Annotation sefAnn = cfv.generatePure() ? generateNamedAnnotation(builderImplType.get(), CheckerFrameworkVersion.NAME__PURE) : null; + Annotation overrideAnn = makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, job.builderType.get()); + Annotation rrAnn = job.checkerFramework.generateReturnsReceiver() ? generateNamedAnnotation(job.builderType.get(), CheckerFrameworkVersion.NAME__RETURNS_RECEIVER) : null; + Annotation sefAnn = job.checkerFramework.generatePure() ? generateNamedAnnotation(job.builderType.get(), CheckerFrameworkVersion.NAME__PURE) : null; if (rrAnn != null && sefAnn != null) out.annotations = new Annotation[] {overrideAnn, rrAnn, sefAnn}; else if (rrAnn != null) out.annotations = new Annotation[] {overrideAnn, rrAnn}; else if (sefAnn != null) out.annotations = new Annotation[] {overrideAnn, sefAnn}; else out.annotations = new Annotation[] {overrideAnn}; - out.returnType = namePlusTypeParamsToTypeReference(builderImplType, typeParams, p); + out.returnType = namePlusTypeParamsToTypeReference(job.builderType, job.typeParams, job.getPos()); out.statements = new Statement[] {new ReturnStatement(new ThisReference(0, 0), 0, 0)}; return out; } - private MethodDeclaration generateAbstractBuildMethod(CheckerFrameworkVersion cfv, EclipseNode builderType, String methodName, List<BuilderFieldData> builderFields, boolean override, - String classGenericName, ASTNode source) { - - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); + private MethodDeclaration generateAbstractBuildMethod(BuilderJob job, boolean override, String classGenericName) { + MethodDeclaration out = job.createNewMethodDeclaration(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract | ExtraCompilerModifiers.AccSemicolonBody; - out.selector = methodName.toCharArray(); + out.selector = job.buildMethodName.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = new SingleTypeReference(classGenericName.toCharArray(), 0); - Annotation overrideAnn = override ? makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source) : null; - Annotation sefAnn = cfv.generateSideEffectFree() ? generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE): null; + Annotation overrideAnn = override ? makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, job.source) : null; + Annotation sefAnn = job.checkerFramework.generateSideEffectFree() ? generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE): null; if (overrideAnn != null && sefAnn != null) out.annotations = new Annotation[] {overrideAnn, sefAnn}; else if (overrideAnn != null) out.annotations = new Annotation[] {overrideAnn}; else if (sefAnn != null) out.annotations = new Annotation[] {sefAnn}; - out.receiver = HandleBuilder.generateBuildReceiver(cfv, builderType, builderFields, source); - out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + out.receiver = HandleBuilder.generateBuildReceiver(job); + out.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null); return out; } - private MethodDeclaration generateBuildMethod(CheckerFrameworkVersion cfv, EclipseNode builderType, String name, TypeReference returnType, List<BuilderFieldData> builderFields, ASTNode source) { - MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); + private MethodDeclaration generateBuildMethod(BuilderJob job, TypeReference returnType) { + MethodDeclaration out = job.createNewMethodDeclaration(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List<Statement> statements = new ArrayList<Statement>(); out.modifiers = ClassFileConstants.AccPublic; - out.selector = name.toCharArray(); + out.selector = job.buildMethodName.toCharArray(); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = returnType; - Annotation overrideAnn = makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source); - Annotation sefAnn = cfv.generateSideEffectFree() ? generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE): null; + Annotation overrideAnn = makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, job.source); + Annotation sefAnn = job.checkerFramework.generateSideEffectFree() ? generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE): null; if (sefAnn != null) out.annotations = new Annotation[] {overrideAnn, sefAnn}; else out.annotations = new Annotation[] {overrideAnn}; @@ -858,43 +900,44 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { allocationStatement.arguments = new Expression[] {new ThisReference(0, 0)}; statements.add(new ReturnStatement(allocationStatement, 0, 0)); out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[0]); - out.receiver = HandleBuilder.generateBuildReceiver(cfv, builderType, builderFields, source); - createRelevantNonNullAnnotation(builderType, out); - out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + out.receiver = HandleBuilder.generateBuildReceiver(job); + createRelevantNonNullAnnotation(job.builderType, out); + out.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null); return out; } - private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType, ASTNode source) { + private MethodDeclaration generateCleanMethod(BuilderJob job) { List<Statement> statements = new ArrayList<Statement>(); - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, builderType, statements); + bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, job.builderType, statements); } } FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); thisUnclean.receiver = new ThisReference(0, 0); statements.add(new Assignment(thisUnclean, new FalseLiteral(0, 0), 0)); - MethodDeclaration decl = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); + MethodDeclaration decl = job.createNewMethodDeclaration(); + //new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); decl.selector = CLEAN_METHOD_NAME; decl.modifiers = ClassFileConstants.AccPrivate; decl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; decl.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0); decl.statements = statements.toArray(new Statement[0]); - decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + decl.traverse(new SetGeneratedByVisitor(job.source), (ClassScope) null); return decl; } - private void generateBuilderFields(EclipseNode builderType, List<BuilderFieldData> builderFields, ASTNode source) { + private void generateBuilderFields(BuilderJob job) { List<EclipseNode> existing = new ArrayList<EclipseNode>(); - for (EclipseNode child : builderType.down()) { + for (EclipseNode child : job.builderType.down()) { if (child.getKind() == Kind.FIELD) existing.add(child); } - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType)); + bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, job.builderType)); } else { EclipseNode field = null, setFlag = null; for (EclipseNode exists : existing) { @@ -908,23 +951,23 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; fd.modifiers = ClassFileConstants.AccPrivate; fd.type = copyType(bfd.type); - fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); - field = injectFieldAndMarkGenerated(builderType, fd); + fd.traverse(new SetGeneratedByVisitor(job.source), (MethodScope) null); + field = injectFieldAndMarkGenerated(job.builderType, fd); } if (setFlag == null && bfd.nameOfSetFlag != null) { FieldDeclaration fd = new FieldDeclaration(bfd.nameOfSetFlag, 0, 0); fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; fd.modifiers = ClassFileConstants.AccPrivate; fd.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); - fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); - injectFieldAndMarkGenerated(builderType, fd); + fd.traverse(new SetGeneratedByVisitor(job.source), (MethodScope) null); + injectFieldAndMarkGenerated(job.builderType, fd); } bfd.createdFields.add(field); } } } - private void generateSetterMethodsForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, final String builderGenericName, String setterPrefix) { + private void generateSetterMethodsForBuilder(BuilderJob job, BuilderFieldData bfd, final String builderGenericName, String setterPrefix) { boolean deprecate = isFieldDeprecated(bfd.originalFieldNode); TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() { @@ -943,15 +986,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { }; if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - generateSimpleSetterMethodForBuilder(cfv, builderType, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode, bfd.annotations, bfd.originalFieldNode, setterPrefix); + generateSimpleSetterMethodForBuilder(job, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), bfd.annotations, bfd.originalFieldNode, setterPrefix); } else { - bfd.singularData.getSingularizer().generateMethods(cfv, bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC); + bfd.singularData.getSingularizer().generateMethods(job.checkerFramework, bfd.singularData, deprecate, job.builderType, true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC); } } - - private void generateSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, TypeReference returnType, Statement returnStatement, EclipseNode sourceNode, Annotation[] annosOnParam, EclipseNode originalFieldNode, String setterPrefix) { - TypeDeclaration td = (TypeDeclaration) builderType.get(); - ASTNode source = sourceNode.get(); + + private void generateSimpleSetterMethodForBuilder(BuilderJob job, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, TypeReference returnType, Statement returnStatement, Annotation[] annosOnParam, EclipseNode originalFieldNode, String setterPrefix) { + TypeDeclaration td = (TypeDeclaration) job.builderType.get(); AbstractMethodDeclaration[] existing = td.methods; if (existing == null) existing = EMPTY_METHODS; int len = existing.length; @@ -965,23 +1007,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } List<Annotation> methodAnnsList = Arrays.asList(EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode)); - if (cfv.generateReturnsReceiver()) { + if (job.checkerFramework.generateReturnsReceiver()) { methodAnnsList = new ArrayList<Annotation>(methodAnnsList); - methodAnnsList.add(generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER)); + methodAnnsList.add(generateNamedAnnotation(job.source, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER)); } MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, paramName, nameOfSetFlag, returnType, returnStatement, ClassFileConstants.AccPublic, - sourceNode, methodAnnsList, annosOnParam != null ? Arrays.asList(copyAnnotations(source, annosOnParam)) : Collections.<Annotation>emptyList()); - if (cfv.generateCalledMethods()) { - char[][] nameNotCalled = fromQualifiedName(CheckerFrameworkVersion.NAME__NOT_CALLED); - SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameNotCalled, poss(source, nameNotCalled.length)), source.sourceStart); - ann.memberValue = new StringLiteral(setterName.toCharArray(), 0, 0, 0); - - QualifiedTypeReference typeReference = (QualifiedTypeReference) generateTypeReference(builderType, 0); - typeReference.annotations = new Annotation[typeReference.tokens.length][]; - typeReference.annotations[0] = new Annotation[] {ann}; - setter.receiver = new Receiver(new char[] { 't', 'h', 'i', 's' }, 0, typeReference, null, Modifier.FINAL); - } - injectMethod(builderType, setter); + job.sourceNode, methodAnnsList, annosOnParam != null ? Arrays.asList(copyAnnotations(job.source, annosOnParam)) : Collections.<Annotation>emptyList()); + if (job.checkerFramework.generateCalledMethods()) setter.receiver = generateNotCalledReceiver(job, setterName); + injectMethod(job.builderType, setter); } private void addObtainVia(BuilderFieldData bfd, EclipseNode node) { @@ -1069,6 +1102,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { // 3. Add used type names. if (td.fields != null) { for (FieldDeclaration field : td.fields) { + if (field instanceof Initializer) continue; char[][] typeName = field.type.getTypeName(); if (typeName.length >= 1) // Add the first token, because only that can collide. usedNames.add(String.valueOf(typeName[0])); @@ -1171,7 +1205,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { if ((def.bits & ASTNode.IsDefaultConstructor) != 0) continue; if (!def.isConstructor()) continue; if (isTolerate(type, def)) continue; - if (def.arguments.length != 1) continue; + if (def.arguments == null || def.arguments.length != 1) continue; // Cannot use typeMatches() here, because the parameter could be fully-qualified, partially-qualified, or not qualified. // A string-compare of the last part should work. If it's a false-positive, users could still @Tolerate it. diff --git a/src/core/lombok/eclipse/handlers/HandleSynchronized.java b/src/core/lombok/eclipse/handlers/HandleSynchronized.java index 37d6755c..18d1e7b7 100644 --- a/src/core/lombok/eclipse/handlers/HandleSynchronized.java +++ b/src/core/lombok/eclipse/handlers/HandleSynchronized.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,7 @@ import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression; @@ -51,12 +52,11 @@ import org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; -import org.mangosdk.spi.ProviderFor; /** * Handles the {@code lombok.Synchronized} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides @DeferUntilPostDiet @HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleSynchronized extends EclipseAnnotationHandler<Synchronized> { @@ -153,7 +153,6 @@ public class HandleSynchronized extends EclipseAnnotationHandler<Synchronized> { annotationNode.addError("@Synchronized is legal only on methods in classes and enums."); return; } - boolean[] isStatic = { method.isStatic() }; char[] lockName = createLockField(annotation, annotationNode, isStatic, true); if (lockName == null) return; diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index 9b58474c..72491277 100644 --- a/src/core/lombok/eclipse/handlers/HandleToString.java +++ b/src/core/lombok/eclipse/handlers/HandleToString.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -51,7 +51,6 @@ import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; -import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; import lombok.ConfigurationKeys; @@ -66,11 +65,12 @@ import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.spi.Provides; /** * Handles the {@code ToString} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleToString extends EclipseAnnotationHandler<ToString> { public void handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString"); diff --git a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java index 338aab0d..9a18b20b 100644 --- a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java +++ b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,8 +25,6 @@ import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; -import java.util.Arrays; - import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; @@ -45,7 +43,6 @@ import org.eclipse.jdt.internal.compiler.ast.ThrowStatement; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; -import org.mangosdk.spi.ProviderFor; import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; @@ -54,12 +51,13 @@ import lombok.core.AST.Kind; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.UtilityClass; +import lombok.spi.Provides; /** * Handles the {@code lombok.experimental.UtilityClass} annotation for eclipse. */ +@Provides @HandlerPriority(-4096) //-2^12; to ensure @FieldDefaults picks up on the 'static' we set here. -@ProviderFor(EclipseAnnotationHandler.class) public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> { @Override public void handle(AnnotationValues<UtilityClass> annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.UTILITY_CLASS_FLAG_USAGE, "@UtilityClass"); @@ -71,7 +69,7 @@ public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> { private static boolean checkLegality(EclipseNode typeNode, EclipseNode errorNode) { if (!isClass(typeNode)) { - errorNode.addError("@UtilityClass is only supported on a class (can't be an interface, enum, annotation, or record)."); + errorNode.addError("@UtilityClass is only supported on a class."); return false; } @@ -153,37 +151,28 @@ public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> { ASTNode source = sourceNode.get(); TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get()); - long p = (long) source.sourceStart << 32 | source.sourceEnd; ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); constructor.modifiers = ClassFileConstants.AccPrivate; constructor.selector = typeDeclaration.name; constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); - constructor.constructorCall.sourceStart = source.sourceStart; - constructor.constructorCall.sourceEnd = source.sourceEnd; constructor.thrownExceptions = null; constructor.typeParameters = null; constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; - constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart; - constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; constructor.arguments = null; - AllocationExpression exception = new AllocationExpression(); - setGeneratedBy(exception, source); long[] ps = new long[JAVA_LANG_UNSUPPORTED_OPERATION_EXCEPTION.length]; - Arrays.fill(ps, p); + AllocationExpression exception = new AllocationExpression(); exception.type = new QualifiedTypeReference(JAVA_LANG_UNSUPPORTED_OPERATION_EXCEPTION, ps); - setGeneratedBy(exception.type, source); exception.arguments = new Expression[] { - new StringLiteral(UNSUPPORTED_MESSAGE, source.sourceStart, source.sourceEnd, 0) + new StringLiteral(UNSUPPORTED_MESSAGE, 0, 0, 0) }; - setGeneratedBy(exception.arguments[0], source); - ThrowStatement throwStatement = new ThrowStatement(exception, source.sourceStart, source.sourceEnd); - setGeneratedBy(throwStatement, source); + ThrowStatement throwStatement = new ThrowStatement(exception, 0, 0); constructor.statements = new Statement[] {throwStatement}; + constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); injectMethod(typeNode, constructor); } } diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java index 3742ac00..ac53e27e 100644 --- a/src/core/lombok/eclipse/handlers/HandleVal.java +++ b/src/core/lombok/eclipse/handlers/HandleVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2018 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,6 +31,7 @@ import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseASTAdapter; import lombok.eclipse.EclipseASTVisitor; import lombok.eclipse.EclipseNode; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; @@ -39,12 +40,11 @@ import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeReference; -import org.mangosdk.spi.ProviderFor; /* * This class just handles 3 basic error cases. The real meat of eclipse 'val' support is in {@code PatchVal} and {@code PatchValEclipse}. */ -@ProviderFor(EclipseASTVisitor.class) +@Provides(EclipseASTVisitor.class) @DeferUntilPostDiet @HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated. public class HandleVal extends EclipseASTAdapter { diff --git a/src/core/lombok/eclipse/handlers/HandleValue.java b/src/core/lombok/eclipse/handlers/HandleValue.java index abea5603..7a73e5ed 100644 --- a/src/core/lombok/eclipse/handlers/HandleValue.java +++ b/src/core/lombok/eclipse/handlers/HandleValue.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,17 +34,17 @@ import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists; import lombok.experimental.NonFinal; +import lombok.spi.Provides; import lombok.Value; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.mangosdk.spi.ProviderFor; /** * Handles the {@code lombok.Value} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@Provides @HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier. public class HandleValue extends EclipseAnnotationHandler<Value> { private HandleFieldDefaults handleFieldDefaults = new HandleFieldDefaults(); @@ -59,13 +59,11 @@ public class HandleValue extends EclipseAnnotationHandler<Value> { Value ann = annotation.getInstance(); EclipseNode typeNode = annotationNode.up(); - TypeDeclaration typeDecl = null; - if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); - if (!isClass(typeNode)) { annotationNode.addError("@Value is only supported on a class."); return; } + TypeDeclaration typeDecl = (TypeDeclaration) typeNode.get(); // Make class final. if (!hasAnnotation(NonFinal.class, typeNode)) { diff --git a/src/core/lombok/eclipse/handlers/HandleWith.java b/src/core/lombok/eclipse/handlers/HandleWith.java index 83357710..bfad682b 100644 --- a/src/core/lombok/eclipse/handlers/HandleWith.java +++ b/src/core/lombok/eclipse/handlers/HandleWith.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2020 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,6 +38,7 @@ import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; @@ -57,9 +58,8 @@ 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.lookup.ExtraCompilerModifiers; -import org.mangosdk.spi.ProviderFor; -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleWith extends EclipseAnnotationHandler<With> { public boolean generateWithForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelWith) { if (checkForTypeLevelWith) { @@ -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..a8d13a84 100644 --- a/src/core/lombok/eclipse/handlers/HandleWithBy.java +++ b/src/core/lombok/eclipse/handlers/HandleWithBy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Project Lombok Authors. + * Copyright (C) 2020-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -51,7 +51,6 @@ import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; -import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; import lombok.ConfigurationKeys; @@ -63,8 +62,9 @@ import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.WithBy; +import lombok.spi.Provides; -@ProviderFor(EclipseAnnotationHandler.class) +@Provides public class HandleWithBy extends EclipseAnnotationHandler<WithBy> { public boolean generateWithByForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelWithBy) { if (checkForTypeLevelWithBy) { @@ -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/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java index 5956c01b..088a4506 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,12 +21,11 @@ */ package lombok.eclipse.handlers.singulars; -import org.mangosdk.spi.ProviderFor; - import lombok.core.LombokImmutableList; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; +import lombok.spi.Provides; -@ProviderFor(EclipseSingularizer.class) +@Provides(EclipseSingularizer.class) public class EclipseGuavaMapSingularizer extends EclipseGuavaSingularizer { // TODO cgcc.ImmutableMultimap, cgcc.ImmutableListMultimap, cgcc.ImmutableSetMultimap // TODO cgcc.ImmutableClassToInstanceMap diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java index 326a9179..1f3603da 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,12 +21,11 @@ */ package lombok.eclipse.handlers.singulars; -import org.mangosdk.spi.ProviderFor; - import lombok.core.LombokImmutableList; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; +import lombok.spi.Provides; -@ProviderFor(EclipseSingularizer.class) +@Provides(EclipseSingularizer.class) public class EclipseGuavaSetListSingularizer extends EclipseGuavaSingularizer { // TODO com.google.common.collect.ImmutableRangeSet // TODO com.google.common.collect.ImmutableMultiset and com.google.common.collect.ImmutableSortedMultiset diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index 395d2e59..7dcf18c9 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -279,7 +279,7 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { protected abstract String getAddMethodName(); protected abstract String getAddAllTypeName(); - protected int getTypeArgumentsCount() { + @Override protected int getTypeArgumentsCount() { return getArgumentSuffixes().size(); } } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java index 4d25811b..ee92ad5a 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,12 +21,11 @@ */ package lombok.eclipse.handlers.singulars; -import org.mangosdk.spi.ProviderFor; - import lombok.core.LombokImmutableList; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; +import lombok.spi.Provides; -@ProviderFor(EclipseSingularizer.class) +@Provides(EclipseSingularizer.class) public class EclipseGuavaTableSingularizer extends EclipseGuavaSingularizer { private static final LombokImmutableList<String> SUFFIXES = LombokImmutableList.of("rowKey", "columnKey", "value"); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java index deab4530..882b7adc 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -156,7 +156,7 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToBuilderSingularSetterAnnotations(data.getAnnotation().up())); md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class); - + if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md); data.setGeneratedByRecursive(md); HandleNonNull.INSTANCE.fix(injectMethod(builderType, md)); @@ -194,9 +194,13 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToSetterAnnotations(data.getAnnotation().up())); md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class); - + if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md); data.setGeneratedByRecursive(md); injectMethod(builderType, md); } + + @Override protected int getTypeArgumentsCount() { + return 1; + } } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java index bf50aef6..0d0f3130 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,6 +31,7 @@ import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Assignment; @@ -45,10 +46,8 @@ import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.SwitchStatement; import org.eclipse.jdt.internal.compiler.ast.TypeReference; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(EclipseSingularizer.class) +@Provides(EclipseSingularizer.class) public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingularizer { @Override public LombokImmutableList<String> getSupportedTypes() { return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.lang.Iterable"); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index 1a40369d..a766612f 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -50,7 +50,6 @@ import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; import lombok.core.LombokImmutableList; @@ -62,8 +61,9 @@ import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker; import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker; import lombok.eclipse.handlers.HandleNonNull; +import lombok.spi.Provides; -@ProviderFor(EclipseSingularizer.class) +@Provides(EclipseSingularizer.class) public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer { @Override public LombokImmutableList<String> getSupportedTypes() { return LombokImmutableList.of("java.util.Map", "java.util.SortedMap", "java.util.NavigableMap"); @@ -349,4 +349,8 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, true, true, false, true, "TreeMap", builderVariable)); } } + + @Override protected int getTypeArgumentsCount() { + return 2; + } } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java index 2076ec7d..a432f3fe 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,11 +27,11 @@ import lombok.core.LombokImmutableList; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; +import lombok.spi.Provides; import org.eclipse.jdt.internal.compiler.ast.Statement; -import org.mangosdk.spi.ProviderFor; -@ProviderFor(EclipseSingularizer.class) +@Provides(EclipseSingularizer.class) public class EclipseJavaUtilSetSingularizer extends EclipseJavaUtilListSetSingularizer { @Override public LombokImmutableList<String> getSupportedTypes() { return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet"); diff --git a/src/core/lombok/javac/CompilerMessageSuppressor.java b/src/core/lombok/javac/CompilerMessageSuppressor.java index 02dc6c26..7d7b81e6 100644 --- a/src/core/lombok/javac/CompilerMessageSuppressor.java +++ b/src/core/lombok/javac/CompilerMessageSuppressor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 The Project Lombok Authors. + * Copyright (C) 2011-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -226,7 +226,7 @@ public final class CompilerMessageSuppressor { ListBuffer<Object> newDeferredDiagnostics = new ListBuffer<Object>(); for (Object diag_ : deferredDiagnostics) { if (!(diag_ instanceof JCDiagnostic)) { - newDeferredDiagnostics.add(diag_); + newDeferredDiagnostics.append(diag_); continue; } JCDiagnostic diag = (JCDiagnostic) diag_; @@ -234,7 +234,7 @@ public final class CompilerMessageSuppressor { if (here >= startPos && here < endPos && diag.getSource() == sourcefile) { // We eliminate it } else { - newDeferredDiagnostics.add(diag); + newDeferredDiagnostics.append(diag); } } field.set(receiver, newDeferredDiagnostics); diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java index 9e7b8fe5..c2bf8512 100644 --- a/src/core/lombok/javac/HandlerLibrary.java +++ b/src/core/lombok/javac/HandlerLibrary.java @@ -65,7 +65,7 @@ public class HandlerLibrary { /** * Creates a new HandlerLibrary that will report any problems or errors to the provided messager. - * You probably want to use {@link #load(Messager)} instead. + * You probably want to use {@link #load(Messager, Trees)} instead. */ public HandlerLibrary(Messager messager) { ConfigurationKeysLoader.LoaderLoader.loadAllConfigurationKeys(); diff --git a/src/core/lombok/javac/Javac6BasedLombokOptions.java b/src/core/lombok/javac/Javac6BasedLombokOptions.java index fc73181c..93f448a7 100644 --- a/src/core/lombok/javac/Javac6BasedLombokOptions.java +++ b/src/core/lombok/javac/Javac6BasedLombokOptions.java @@ -58,7 +58,7 @@ public class Javac6BasedLombokOptions extends LombokOptions { @Override public void putJavacOption(String optionName, String value) { try { - options_put.invoke(this, optionName_valueOf.invoke(null, optionName), value); + Permit.invoke(options_put, this, Permit.invoke(optionName_valueOf, null, optionName), value); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Can't initialize Javac6-based lombok options due to reflection issue.", e); } catch (InvocationTargetException e) { diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java index 86ff7646..f58de60f 100644 --- a/src/core/lombok/javac/JavacAST.java +++ b/src/core/lombok/javac/JavacAST.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 @@ -21,6 +21,7 @@ */ package lombok.javac; +import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URI; @@ -110,14 +111,97 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { return memoizedAbsoluteFileLocation; } + private static Class<?> wrappedFileObjectClass, sbtJavaFileObjectClass, sbtMappedVirtualFileClass, sbtOptionClass; + private static Field wrappedFileObjectField, sbtJavaFileObjectField, sbtMappedVirtualFilePathField, sbtMappedVirtualFileRootsField, sbtOptionField; + private static Method sbtMapGetMethod; + public static URI getAbsoluteFileLocation(JCCompilationUnit cu) { try { - return cu.sourcefile.toUri(); + URI uri = cu.sourcefile.toUri(); + String fn = uri.toString(); + if (fn.startsWith("file:")) return uri; + URI sbtUri = tryGetSbtFile(cu.sourcefile); + if (sbtUri != null) return sbtUri; + return uri; + } catch (Exception e) { + return null; + } + } + + private static URI tryGetSbtFile(JavaFileObject sourcefile) { + try { + return tryGetSbtFile_(sourcefile); } catch (Exception e) { return null; } } + private static URI tryGetSbtFile_(JavaFileObject sourcefile) throws Exception { + Class<?> c = sourcefile.getClass(); + String cn; + + if (wrappedFileObjectClass == null) { + if (!c.getName().equals("com.sun.tools.javac.api.ClientCodeWrapper$WrappedJavaFileObject")) return null; + wrappedFileObjectClass = c; + } + if (c != wrappedFileObjectClass) return null; + + if (wrappedFileObjectField == null) wrappedFileObjectField = Permit.permissiveGetField(wrappedFileObjectClass.getSuperclass(), "clientFileObject"); + if (wrappedFileObjectField == null) return null; + Object fileObject = wrappedFileObjectField.get(sourcefile); + c = fileObject.getClass(); + + if (sbtJavaFileObjectClass == null) { + cn = c.getName(); + if (!cn.startsWith("sbt.") || !cn.endsWith("JavaFileObject")) return null; + sbtJavaFileObjectClass = c; + } + if (sbtJavaFileObjectClass != c) return null; + if (sbtJavaFileObjectField == null) sbtJavaFileObjectField = Permit.permissiveGetField(sbtJavaFileObjectClass, "underlying"); + if (sbtJavaFileObjectField == null) return null; + + Object mappedVirtualFile = sbtJavaFileObjectField.get(fileObject); + c = mappedVirtualFile.getClass(); + + if (sbtMappedVirtualFileClass == null) { + cn = c.getName(); + if (!cn.startsWith("sbt.") || !cn.endsWith("MappedVirtualFile")) return null; + sbtMappedVirtualFileClass = c; + } + if (sbtMappedVirtualFilePathField == null) sbtMappedVirtualFilePathField = Permit.permissiveGetField(sbtMappedVirtualFileClass, "encodedPath"); + if (sbtMappedVirtualFilePathField == null) return null; + if (sbtMappedVirtualFileRootsField == null) sbtMappedVirtualFileRootsField = Permit.permissiveGetField(sbtMappedVirtualFileClass, "rootPathsMap"); + if (sbtMappedVirtualFileRootsField == null) return null; + + String encodedPath = (String) sbtMappedVirtualFilePathField.get(mappedVirtualFile); + if (!encodedPath.startsWith("${")) { + File maybeAbsoluteFile = new File(encodedPath); + if (maybeAbsoluteFile.exists()) { + return maybeAbsoluteFile.toURI(); + } else { + return null; + } + } + int idx = encodedPath.indexOf('}'); + if (idx == -1) return null; + String base = encodedPath.substring(2, idx); + Object roots = sbtMappedVirtualFileRootsField.get(mappedVirtualFile); + if (sbtMapGetMethod == null) sbtMapGetMethod = Permit.getMethod(roots.getClass(), "get", Object.class); + if (sbtMapGetMethod == null) return null; + + Object option = sbtMapGetMethod.invoke(roots, base); + c = option.getClass(); + if (sbtOptionClass == null) { + if (c.getName().equals("scala.Some")) sbtOptionClass = c; + } + if (c != sbtOptionClass) return null; + if (sbtOptionField == null) sbtOptionField = Permit.permissiveGetField(sbtOptionClass, "value"); + if (sbtOptionField == null) return null; + + Object path = sbtOptionField.get(option); + return new File(path.toString() + encodedPath.substring(idx + 1)).toURI(); + } + private static String sourceName(JCCompilationUnit cu) { return cu.sourcefile == null ? null : cu.sourcefile.toString(); } @@ -397,11 +481,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { } private JCTree getBody(JCTree jcTree) { - try { - return (JCTree) getBodyMethod(jcTree.getClass()).invoke(jcTree); - } catch (Exception e) { - throw Javac.sneakyThrow(e); - } + return (JCTree) Permit.invokeSneaky(getBodyMethod(jcTree.getClass()), jcTree); } private final static ConcurrentMap<Class<?>, Method> getBodyMethods = new ConcurrentHashMap<Class<?>, Method>(); @@ -662,42 +742,26 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { } @Override void error1(DiagnosticPosition pos, String message) { - try { - Object error = this.errorKey.invoke(diags, PROC_MESSAGER, new Object[] { message }); - errorMethod.invoke(log, multiple, pos, error); - } catch (Throwable t) { - //t.printStackTrace(); - } + Object error = Permit.invokeSneaky(this.errorKey, diags, PROC_MESSAGER, new Object[] { message }); + if (error != null) Permit.invokeSneaky(errorMethod, log, multiple, pos, error); } @Override void warning1(DiagnosticPosition pos, String message) { - try { - Object warning = this.warningKey.invoke(diags, PROC_MESSAGER, new Object[] { message }); - warningMethod.invoke(log, pos, warning); - } catch (Throwable t) { - //t.printStackTrace(); - } + Object warning = Permit.invokeSneaky(this.warningKey, diags, PROC_MESSAGER, new Object[] { message }); + if (warning != null) Permit.invokeSneaky(warningMethod, log, pos, warning); } @Override void mandatoryWarning1(DiagnosticPosition pos, String message) { - try { - Object warning = this.warningKey.invoke(diags, PROC_MESSAGER, new Object[] { message }); - mandatoryWarningMethod.invoke(log, pos, warning); - } catch (Throwable t) { - //t.printStackTrace(); - } + Object warning = Permit.invokeSneaky(this.warningKey, diags, PROC_MESSAGER, new Object[] { message }); + if (warning != null) Permit.invokeSneaky(mandatoryWarningMethod, log, pos, warning); } @Override void note(DiagnosticPosition pos, String message) { - try { - Object note = this.noteKey.invoke(diags, PROC_MESSAGER, new Object[] { message }); - noteMethod.invoke(log, pos, note); - } catch (Throwable t) { - //t.printStackTrace(); - } + Object note = Permit.invokeSneaky(this.noteKey, diags, PROC_MESSAGER, new Object[] { message }); + if (note != null) Permit.invokeSneaky(noteMethod, log, pos, note); } } } diff --git a/src/core/lombok/javac/JavacASTVisitor.java b/src/core/lombok/javac/JavacASTVisitor.java index 9b67dda3..e8fd295c 100644 --- a/src/core/lombok/javac/JavacASTVisitor.java +++ b/src/core/lombok/javac/JavacASTVisitor.java @@ -22,6 +22,7 @@ package lombok.javac; import java.io.PrintStream; +import java.lang.reflect.Field; import com.sun.source.util.Trees; import com.sun.tools.javac.code.Flags; @@ -32,6 +33,7 @@ import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.util.List; /** * Implement so you can ask any JavacAST.LombokNode to traverse depth-first through all children, @@ -223,6 +225,26 @@ public interface JavacASTVisitor { } else type = "METHOD"; print("<%s %s> %s returns: %s", type, method.name, printFlags(method.mods.flags), method.restype); indent++; + JCVariableDecl recv; + try { + Field f = JCMethodDecl.class.getField("recvparam"); + recv = (JCVariableDecl) f.get(method); + } catch (Exception ignore) { + recv = null; + } + + if (recv != null) { + List<JCAnnotation> annotations = recv.mods.annotations; + if (recv.mods != null) annotations = recv.mods.annotations; + boolean innerContent = annotations != null && annotations.isEmpty(); + print("<RECEIVER-PARAM (%s) %s %s%s> %s", recv.vartype == null ? "null" : recv.vartype.getClass().toString(), recv.vartype, recv.name, innerContent ? "" : " /", printFlags(recv.mods.flags)); + if (innerContent) { + indent++; + for (JCAnnotation ann : annotations) print("<ANNOTATION: %s />", ann); + indent--; + print("</RECEIVER-PARAM>"); + } + } if (printContent) { if (method.body == null) print("(ABSTRACT)"); else print("%s", method.body); @@ -237,11 +259,11 @@ public interface JavacASTVisitor { @Override public void endVisitMethod(JavacNode node, JCMethodDecl method) { if (printContent) disablePrinting--; indent--; - print("</%s %s>", "XMETHOD", method.name); + print("</%s %s>", "METHOD", method.name); } @Override public void visitMethodArgument(JavacNode node, JCVariableDecl arg, JCMethodDecl method) { - print("<METHODARG %s %s> %s", arg.vartype, arg.name, printFlags(arg.mods.flags)); + print("<METHODARG (%s) %s %s> %s", arg.vartype.getClass().toString(), arg.vartype, arg.name, printFlags(arg.mods.flags)); indent++; } diff --git a/src/core/lombok/javac/JavacNode.java b/src/core/lombok/javac/JavacNode.java index e648cd51..035f7c53 100644 --- a/src/core/lombok/javac/JavacNode.java +++ b/src/core/lombok/javac/JavacNode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java index 6b2f4637..e96079e0 100644 --- a/src/core/lombok/javac/JavacResolution.java +++ b/src/core/lombok/javac/JavacResolution.java @@ -25,7 +25,6 @@ import static lombok.javac.Javac.*; import static lombok.javac.JavacTreeMaker.TypeTag.typeTag; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayDeque; import java.util.Collection; @@ -65,7 +64,6 @@ import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Name; -import lombok.Lombok; import lombok.core.debug.AssertionLogger; import lombok.permit.Permit; @@ -73,6 +71,16 @@ public class JavacResolution { private final Attr attr; private final CompilerMessageSuppressor messageSuppressor; + private static final Method isLocal; + + static { + Method local = Permit.permissiveGetMethod(TypeSymbol.class, "isLocal"); + if (local == null) { + local = Permit.permissiveGetMethod(TypeSymbol.class, "isDirectlyOrIndirectlyLocal"); + } + isLocal = local; + } + public JavacResolution(Context context) { attr = Attr.instance(context); messageSuppressor = new CompilerMessageSuppressor(context); @@ -251,27 +259,27 @@ public class JavacResolution { private static class ReflectiveAccess { private static Method UPPER_BOUND; + private static Throwable initError; static { Method upperBound = null; try { upperBound = Permit.getMethod(Types.class, "upperBound", Type.class); - } catch (Throwable ignore) {} + } catch (Throwable e) { + initError = e; + } + if (upperBound == null) try { upperBound = Permit.getMethod(Types.class, "wildUpperBound", Type.class); - } catch (Throwable ignore) {} + } catch (Throwable e) { + initError = e; + } UPPER_BOUND = upperBound; } public static Type Types_upperBound(Types types, Type type) { - try { - return (Type) UPPER_BOUND.invoke(types, type); - } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e.getCause()); - } catch (Exception e) { - throw Lombok.sneakyThrow(e); - } + return (Type) Permit.invokeSneaky(initError, UPPER_BOUND, types, type); } } @@ -354,6 +362,15 @@ public class JavacResolution { return a.compareTo(b); } + private static boolean isLocalType(TypeSymbol symbol) { + try { + return (Boolean) Permit.invoke(isLocal, symbol); + } + catch (Exception e) { + return false; + } + } + private static JCExpression typeToJCTree0(Type type, JavacAST ast, boolean allowCompound, boolean allowVoid, boolean allowCapture) throws TypeNotConvertibleException { // NB: There's such a thing as maker.Type(type), but this doesn't work very well; it screws up anonymous classes, captures, and adds an extra prefix dot for some reason too. // -- so we write our own take on that here. @@ -452,7 +469,7 @@ public class JavacResolution { } String qName; - if (symbol.isLocal()) { + if (isLocalType(symbol)) { qName = symbol.getSimpleName().toString(); } else if (symbol.type != null && symbol.type.getEnclosingType() != null && typeTag(symbol.type.getEnclosingType()).equals(typeTag("CLASS"))) { replacement = typeToJCTree0(type.getEnclosingType(), ast, false, false, false); diff --git a/src/core/lombok/javac/apt/InterceptingJavaFileObject.java b/src/core/lombok/javac/apt/InterceptingJavaFileObject.java index 368928d4..75cf0713 100644 --- a/src/core/lombok/javac/apt/InterceptingJavaFileObject.java +++ b/src/core/lombok/javac/apt/InterceptingJavaFileObject.java @@ -26,7 +26,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.nio.charset.CharsetDecoder; @@ -35,9 +34,9 @@ import javax.lang.model.element.Modifier; import javax.lang.model.element.NestingKind; import javax.tools.JavaFileObject; -import lombok.Lombok; import lombok.core.DiagnosticsReceiver; import lombok.core.PostCompiler; +import lombok.permit.Permit; final class InterceptingJavaFileObject implements LombokFileObject { private final JavaFileObject delegate; @@ -63,16 +62,8 @@ final class InterceptingJavaFileObject implements LombokFileObject { } @Override public CharsetDecoder getDecoder(boolean ignoreEncodingErrors) { - if (decoderMethod == null) { - throw new UnsupportedOperationException(); - } - try { - return (CharsetDecoder) decoderMethod.invoke(delegate, ignoreEncodingErrors); - } catch (IllegalAccessException e) { - throw Lombok.sneakyThrow(e); - } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e); - } + if (decoderMethod == null) throw new UnsupportedOperationException(); + return (CharsetDecoder) Permit.invokeSneaky(decoderMethod, delegate, ignoreEncodingErrors); } @Override public boolean equals(Object obj) { diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java index 852e5de6..6552f15b 100644 --- a/src/core/lombok/javac/apt/LombokProcessor.java +++ b/src/core/lombok/javac/apt/LombokProcessor.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,8 +24,10 @@ package lombok.javac.apt; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; @@ -61,6 +63,8 @@ import lombok.core.CleanupRegistry; import lombok.core.DiagnosticsReceiver; import lombok.javac.JavacTransformer; import lombok.permit.Permit; +import lombok.permit.dummy.Parent; +import sun.misc.Unsafe; /** * This Annotation Processor is the standard injection mechanism for lombok-enabling the javac compiler. @@ -154,10 +158,10 @@ public class LombokProcessor extends AbstractProcessor { disablePartialReparseInNetBeansEditor(context); try { Method keyMethod = Permit.getMethod(Context.class, "key", Class.class); - Object key = keyMethod.invoke(context, JavaFileManager.class); + Object key = Permit.invoke(keyMethod, context, JavaFileManager.class); Field htField = Permit.getField(Context.class, "ht"); @SuppressWarnings("unchecked") - Map<Object,Object> ht = (Map<Object,Object>) htField.get(context); + Map<Object,Object> ht = (Map<Object,Object>) Permit.get(htField, context); final JavaFileManager originalFiler = (JavaFileManager) ht.get(key); if (!(originalFiler instanceof InterceptingJavaFileManager)) { final Messager messager = processingEnv.getMessager(); @@ -180,10 +184,10 @@ public class LombokProcessor extends AbstractProcessor { private void replaceFileManagerJdk9(Context context, JavaFileManager newFiler) { try { - JavaCompiler compiler = (JavaCompiler) Permit.getMethod(JavaCompiler.class, "instance", Context.class).invoke(null, context); + JavaCompiler compiler = (JavaCompiler) Permit.invoke(Permit.getMethod(JavaCompiler.class, "instance", Context.class), null, context); try { Field fileManagerField = Permit.getField(JavaCompiler.class, "fileManager"); - fileManagerField.set(compiler, newFiler); + Permit.set(fileManagerField, compiler, newFiler); } catch (Exception e) {} @@ -191,7 +195,7 @@ public class LombokProcessor extends AbstractProcessor { Field writerField = Permit.getField(JavaCompiler.class, "writer"); ClassWriter writer = (ClassWriter) writerField.get(compiler); Field fileManagerField = Permit.getField(ClassWriter.class, "fileManager"); - fileManagerField.set(writer, newFiler); + Permit.set(fileManagerField, writer, newFiler); } catch (Exception e) {} } @@ -213,8 +217,8 @@ public class LombokProcessor extends AbstractProcessor { private void disablePartialReparseInNetBeansEditor(Context context) { try { Class<?> cancelServiceClass = Class.forName("com.sun.tools.javac.util.CancelService"); - Method cancelServiceInstace = Permit.getMethod(cancelServiceClass, "instance", Context.class); - Object cancelService = cancelServiceInstace.invoke(null, context); + Method cancelServiceInstance = Permit.getMethod(cancelServiceClass, "instance", Context.class); + Object cancelService = Permit.invoke(cancelServiceInstance, null, context); if (cancelService == null) return; Field parserField = Permit.getField(cancelService.getClass(), "parser"); Object parser = parserField.get(cancelService); @@ -386,8 +390,8 @@ public class LombokProcessor extends AbstractProcessor { try { if (qualifiedNamableClass == null) qualifiedNamableClass = Class.forName("javax.lang.model.element.QualifiedNamable"); if (!qualifiedNamableClass.isInstance(element)) return null; - if (qualifiedNamableQualifiedNameMethod == null) qualifiedNamableQualifiedNameMethod = qualifiedNamableClass.getMethod("getQualifiedName"); - String name = qualifiedNamableQualifiedNameMethod.invoke(element).toString().trim(); + if (qualifiedNamableQualifiedNameMethod == null) qualifiedNamableQualifiedNameMethod = Permit.getMethod(qualifiedNamableClass, "getQualifiedName"); + String name = Permit.invoke(qualifiedNamableQualifiedNameMethod, element).toString().trim(); return name.isEmpty() ? null : name; } catch (ClassNotFoundException e) { return null; @@ -428,22 +432,108 @@ public class LombokProcessor extends AbstractProcessor { * gradle incremental compilation, the delegate ProcessingEnvironment of the gradle wrapper is returned. */ public JavacProcessingEnvironment getJavacProcessingEnvironment(Object procEnv) { + addOpensForLombok(); if (procEnv instanceof JavacProcessingEnvironment) return (JavacProcessingEnvironment) procEnv; // try to find a "delegate" field in the object, and use this to try to obtain a JavacProcessingEnvironment for (Class<?> procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) { - try { - return getJavacProcessingEnvironment(tryGetDelegateField(procEnvClass, procEnv)); - } catch (final Exception e) { - // delegate field was not found, try on superclass - } + Object delegate = tryGetDelegateField(procEnvClass, procEnv); + if (delegate == null) delegate = tryGetProxyDelegateToField(procEnvClass, procEnv); + if (delegate == null) delegate = tryGetProcessingEnvField(procEnvClass, procEnv); + + if (delegate != null) return getJavacProcessingEnvironment(delegate); + // delegate field was not found, try on superclass } processingEnv.getMessager().printMessage(Kind.WARNING, "Can't get the delegate of the gradle IncrementalProcessingEnvironment. Lombok won't work."); return null; } - + + private static Object getOwnModule() { + try { + Method m = Permit.getMethod(Class.class, "getModule"); + return m.invoke(LombokProcessor.class); + } catch (Exception e) { + return null; + } + } + + private static Object getJdkCompilerModule() { + /* call public api: ModuleLayer.boot().findModule("jdk.compiler").get(); + but use reflection because we don't want this code to crash on jdk1.7 and below. + In that case, none of this stuff was needed in the first place, so we just exit via + the catch block and do nothing. + */ + + try { + Class<?> cModuleLayer = Class.forName("java.lang.ModuleLayer"); + Method mBoot = cModuleLayer.getDeclaredMethod("boot"); + Object bootLayer = mBoot.invoke(null); + Class<?> cOptional = Class.forName("java.util.Optional"); + Method mFindModule = cModuleLayer.getDeclaredMethod("findModule", String.class); + Object oCompilerO = mFindModule.invoke(bootLayer, "jdk.compiler"); + return cOptional.getDeclaredMethod("get").invoke(oCompilerO); + } catch (Exception e) { + return null; + } + } + + /** Useful from jdk9 and up; required from jdk16 and up. This code is supposed to gracefully do nothing on jdk8 and below, as this operation isn't needed there. */ + public static void addOpensForLombok() { + Class<?> cModule; + try { + cModule = Class.forName("java.lang.Module"); + } catch (ClassNotFoundException e) { + return; //jdk8-; this is not needed. + } + + Unsafe unsafe = getUnsafe(); + Object jdkCompilerModule = getJdkCompilerModule(); + Object ownModule = getOwnModule(); + String[] allPkgs = { + "com.sun.tools.javac.code", + "com.sun.tools.javac.comp", + "com.sun.tools.javac.file", + "com.sun.tools.javac.main", + "com.sun.tools.javac.model", + "com.sun.tools.javac.parser", + "com.sun.tools.javac.processing", + "com.sun.tools.javac.tree", + "com.sun.tools.javac.util", + "com.sun.tools.javac.jvm", + }; + + try { + Method m = cModule.getDeclaredMethod("implAddOpens", String.class, cModule); + long firstFieldOffset = getFirstFieldOffset(unsafe); + unsafe.putBooleanVolatile(m, firstFieldOffset, true); + for (String p : allPkgs) m.invoke(jdkCompilerModule, p, ownModule); + } catch (Exception ignore) {} + } + + private static long getFirstFieldOffset(Unsafe unsafe) { + try { + return unsafe.objectFieldOffset(Parent.class.getDeclaredField("first")); + } catch (NoSuchFieldException e) { + // can't happen. + throw new RuntimeException(e); + } catch (SecurityException e) { + // can't happen + throw new RuntimeException(e); + } + } + + private static Unsafe getUnsafe() { + try { + Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + return (Unsafe) theUnsafe.get(null); + } catch (Exception e) { + return null; + } + } + /** * This class returns the given filer as a JavacFiler. In case the filer is no * JavacFiler (e.g. the Gradle IncrementalFiler), its "delegate" field is used to get the JavacFiler @@ -454,11 +544,12 @@ public class LombokProcessor extends AbstractProcessor { // try to find a "delegate" field in the object, and use this to check for a JavacFiler for (Class<?> filerClass = filer.getClass(); filerClass != null; filerClass = filerClass.getSuperclass()) { - try { - return getJavacFiler(tryGetDelegateField(filerClass, filer)); - } catch (final Exception e) { - // delegate field was not found, try on superclass - } + Object delegate = tryGetDelegateField(filerClass, filer); + if (delegate == null) delegate = tryGetProxyDelegateToField(filerClass, filer); + if (delegate == null) delegate = tryGetFilerField(filerClass, filer); + + if (delegate != null) return getJavacFiler(delegate); + // delegate field was not found, try on superclass } processingEnv.getMessager().printMessage(Kind.WARNING, @@ -466,7 +557,48 @@ public class LombokProcessor extends AbstractProcessor { return null; } - private Object tryGetDelegateField(Class<?> delegateClass, Object instance) throws Exception { - return Permit.getField(delegateClass, "delegate").get(instance); + /** + * Gradle incremental processing + */ + private Object tryGetDelegateField(Class<?> delegateClass, Object instance) { + try { + return Permit.getField(delegateClass, "delegate").get(instance); + } catch (Exception e) { + return null; + } + } + + /** + * Kotlin incremental processing + */ + private Object tryGetProcessingEnvField(Class<?> delegateClass, Object instance) { + try { + return Permit.getField(delegateClass, "processingEnv").get(instance); + } catch (Exception e) { + return null; + } + } + + /** + * Kotlin incremental processing + */ + private Object tryGetFilerField(Class<?> delegateClass, Object instance) { + try { + return Permit.getField(delegateClass, "filer").get(instance); + } catch (Exception e) { + return null; + } + } + + /** + * IntelliJ IDEA >= 2020.3 + */ + private Object tryGetProxyDelegateToField(Class<?> delegateClass, Object instance) { + try { + InvocationHandler handler = Proxy.getInvocationHandler(instance); + return Permit.getField(handler.getClass(), "val$delegateTo").get(handler); + } catch (Exception e) { + return null; + } } } diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java index f15bf74c..e0de363d 100644 --- a/src/core/lombok/javac/apt/Processor.java +++ b/src/core/lombok/javac/apt/Processor.java @@ -222,7 +222,7 @@ public class Processor extends AbstractProcessor { * We just return the latest version of whatever JDK we run on. Stupid? Yeah, but it's either that or warnings on all versions but 1. */ @Override public SourceVersion getSupportedSourceVersion() { - return SourceVersion.values()[SourceVersion.values().length - 1]; + return SourceVersion.latest(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { diff --git a/src/core/lombok/javac/handlers/HandleAccessors.java b/src/core/lombok/javac/handlers/HandleAccessors.java index 46fe1cd2..60466d78 100644 --- a/src/core/lombok/javac/handlers/HandleAccessors.java +++ b/src/core/lombok/javac/handlers/HandleAccessors.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,8 +24,6 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.tree.JCTree.JCAnnotation; import lombok.ConfigurationKeys; @@ -34,8 +32,9 @@ import lombok.core.HandlerPriority; import lombok.experimental.Accessors; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; +import lombok.spi.Provides; -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(65536) public class HandleAccessors extends JavacAnnotationHandler<Accessors> { @Override public void handle(AnnotationValues<Accessors> annotation, JCAnnotation ast, JavacNode annotationNode) { diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index f9675813..a25baeb0 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2020 The Project Lombok Authors. + * Copyright (C) 2013-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,9 @@ import static lombok.javac.JavacTreeMaker.TypeTag.typeTag; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.lang.model.element.Modifier; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; @@ -55,6 +51,7 @@ import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; @@ -82,18 +79,102 @@ import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc; import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; +import lombok.spi.Provides; -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes. public class HandleBuilder extends JavacAnnotationHandler<Builder> { private HandleConstructor handleConstructor = new HandleConstructor(); + static final String CLEAN_FIELD_NAME = "$lombokUnclean"; + static final String CLEAN_METHOD_NAME = "$lombokClean"; + static final String TO_BUILDER_METHOD_NAME = "toBuilder"; + static final String DEFAULT_PREFIX = "$default$"; + static final String SET_PREFIX = "$set"; + static final String VALUE_PREFIX = "$value"; + static final String BUILDER_TEMP_VAR = "builder"; + static final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type."; + private static final boolean toBoolean(Object expr, boolean defaultValue) { if (expr == null) return defaultValue; if (expr instanceof JCLiteral) return ((Integer) ((JCLiteral) expr).value) != 0; return ((Boolean) expr).booleanValue(); } + static class BuilderJob { + CheckerFrameworkVersion checkerFramework; + JavacNode parentType; + String builderMethodName, buildMethodName; + boolean isStatic; + List<JCTypeParameter> typeParams; + List<JCTypeParameter> builderTypeParams; + JavacNode sourceNode; + java.util.List<BuilderFieldData> builderFields; + AccessLevel accessInners, accessOuters; + boolean oldFluent, oldChain, toBuilder; + + JavacNode builderType; + String builderClassName; + + void init(AnnotationValues<Builder> annValues, Builder ann, JavacNode node) { + accessOuters = ann.access(); + if (accessOuters == null) accessOuters = AccessLevel.PUBLIC; + if (accessOuters == AccessLevel.NONE) { + sourceNode.addError("AccessLevel.NONE is not valid here"); + accessOuters = AccessLevel.PUBLIC; + } + accessInners = accessOuters == AccessLevel.PROTECTED ? AccessLevel.PUBLIC : accessOuters; + + oldFluent = toBoolean(annValues.getActualExpression("fluent"), true); + oldChain = toBoolean(annValues.getActualExpression("chain"), true); + + builderMethodName = ann.builderMethodName(); + buildMethodName = ann.buildMethodName(); + builderClassName = fixBuilderClassName(node, ann.builderClassName()); + toBuilder = ann.toBuilder(); + + if (builderMethodName == null) builderMethodName = "builder"; + if (buildMethodName == null) buildMethodName = "build"; + if (builderClassName == null) builderClassName = ""; + } + + static String fixBuilderClassName(JavacNode node, String override) { + if (override != null && !override.isEmpty()) return override; + override = node.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME); + if (override != null && !override.isEmpty()) return override; + return "*Builder"; + } + + String replaceBuilderClassName(Name name) { + if (builderClassName.indexOf('*') == -1) return builderClassName; + return builderClassName.replace("*", name.toString()); + } + + JCExpression createBuilderParentTypeReference() { + return namePlusTypeParamsToTypeReference(parentType.getTreeMaker(), parentType, typeParams); + } + + Name getBuilderClassName() { + return parentType.toName(builderClassName); + } + + List<JCTypeParameter> copyTypeParams() { + return JavacHandlerUtil.copyTypeParams(sourceNode, typeParams); + } + + Name toName(String name) { + return parentType.toName(name); + } + + Context getContext() { + return parentType.getContext(); + } + + JavacTreeMaker getTreeMaker() { + return parentType.getTreeMaker(); + } + } + static class BuilderFieldData { List<JCAnnotation> annotations; JCExpression type; @@ -111,74 +192,56 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) { + final String BUILDER_NODE_NOT_SUPPORTED_ERR = "@Builder is only supported on classes, records, constructors, and methods."; + handleFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); - CheckerFrameworkVersion cfv = getCheckerFrameworkVersion(annotationNode); - - Builder builderInstance = annotation.getInstance(); - AccessLevel accessForOuters = builderInstance.access(); - if (accessForOuters == null) accessForOuters = AccessLevel.PUBLIC; - if (accessForOuters == AccessLevel.NONE) { - annotationNode.addError("AccessLevel.NONE is not valid here"); - accessForOuters = AccessLevel.PUBLIC; - } - AccessLevel accessForInners = accessForOuters == AccessLevel.PROTECTED ? AccessLevel.PUBLIC : accessForOuters; - - // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them. - boolean fluent = toBoolean(annotation.getActualExpression("fluent"), true); - boolean chain = toBoolean(annotation.getActualExpression("chain"), true); - - String builderMethodName = builderInstance.builderMethodName(); - String buildMethodName = builderInstance.buildMethodName(); - String builderClassName = builderInstance.builderClassName(); - String toBuilderMethodName = "toBuilder"; - boolean toBuilder = builderInstance.toBuilder(); - java.util.List<Name> typeArgsForToBuilder = null; + BuilderJob job = new BuilderJob(); + job.sourceNode = annotationNode; + job.checkerFramework = getCheckerFrameworkVersion(annotationNode); + job.isStatic = true; - if (builderMethodName == null) builderMethodName = "builder"; - if (buildMethodName == null) buildMethodName = "build"; - if (builderClassName == null) builderClassName = ""; + Builder annInstance = annotation.getInstance(); + job.init(annotation, annInstance, annotationNode); + java.util.List<Name> typeArgsForToBuilder = null; boolean generateBuilderMethod; - if (builderMethodName.isEmpty()) { + if (job.builderMethodName.isEmpty()) { generateBuilderMethod = false; - } else if (!checkName("builderMethodName", builderMethodName, annotationNode)) { + } else if (!checkName("builderMethodName", job.builderMethodName, annotationNode)) { return; } else { generateBuilderMethod = true; } - if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; - if (!builderClassName.isEmpty()) { - if (!checkName("builderClassName", builderClassName, annotationNode)) return; - } + if (!checkName("buildMethodName", job.buildMethodName, annotationNode)) return; - // Do not delete the Builder annotation here, we need it for @Jacksonized. + // Do not delete the Builder annotation yet, we need it for @Jacksonized. JavacNode parent = annotationNode.up(); - java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>(); - JCExpression returnType; - List<JCTypeParameter> typeParams = List.nil(); - List<JCExpression> thrownExceptions = List.nil(); + job.builderFields = new ArrayList<BuilderFieldData>(); + JCExpression buildMethodReturnType; + job.typeParams = List.nil(); + List<JCExpression> buildMethodThrownExceptions; Name nameOfBuilderMethod; - JavacNode tdParent; JavacNode fillParametersFrom = parent.get() instanceof JCMethodDecl ? parent : null; boolean addCleaning = false; - boolean isStatic = true; ArrayList<JavacNode> nonFinalNonDefaultedFields = null; - if (builderClassName.isEmpty()) builderClassName = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME); - if (builderClassName == null || builderClassName.isEmpty()) builderClassName = "*Builder"; - boolean replaceNameInBuilderClassName = builderClassName.contains("*"); - if (parent.get() instanceof JCClassDecl) { - tdParent = parent; - JCClassDecl td = (JCClassDecl) tdParent.get(); + if (!isClass(parent) && !isRecord(parent)) { + annotationNode.addError(BUILDER_NODE_NOT_SUPPORTED_ERR); + return; + } + + job.parentType = parent; + JCClassDecl td = (JCClassDecl) parent.get(); + ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>(); boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); - for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { + for (JavacNode fieldNode : HandleConstructor.findAllFields(parent, true)) { JCVariableDecl fd = (JCVariableDecl) fieldNode.get(); JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, false); boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); @@ -189,7 +252,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { bfd.builderFieldName = bfd.name; bfd.annotations = findCopyableAnnotations(fieldNode); bfd.type = fd.vartype; - bfd.singularData = getSingularData(fieldNode, builderInstance.setterPrefix()); + bfd.singularData = getSingularData(fieldNode, annInstance.setterPrefix()); bfd.originalFieldNode = fieldNode; if (bfd.singularData != null && isDefault != null) { @@ -211,28 +274,30 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } if (isDefault != null) { - bfd.nameOfDefaultProvider = parent.toName("$default$" + bfd.name); - bfd.nameOfSetFlag = parent.toName(bfd.name + "$set"); - bfd.builderFieldName = parent.toName(bfd.name + "$value"); + bfd.nameOfDefaultProvider = parent.toName(DEFAULT_PREFIX + bfd.name); + bfd.nameOfSetFlag = parent.toName(bfd.name + SET_PREFIX); + bfd.builderFieldName = parent.toName(bfd.name + VALUE_PREFIX); JCMethodDecl md = generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode, td.typarams); - recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); - if (md != null) injectMethod(tdParent, md); + recursiveSetGeneratedBy(md, annotationNode); + if (md != null) injectMethod(parent, md); } addObtainVia(bfd, fieldNode); - builderFields.add(bfd); + job.builderFields.add(bfd); allFields.append(fieldNode); } - if (!isRecord(tdParent)) { - handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); + if (!isRecord(parent)) { + // Records ship with a canonical constructor that acts as @AllArgsConstructor - just use that one. + + handleConstructor.generateConstructor(parent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); } - returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), tdParent, td.typarams); - typeParams = td.typarams; - thrownExceptions = List.nil(); + buildMethodReturnType = namePlusTypeParamsToTypeReference(parent.getTreeMaker(), parent, td.typarams); + job.typeParams = job.builderTypeParams = td.typarams; + buildMethodThrownExceptions = List.nil(); nameOfBuilderMethod = null; - if (replaceNameInBuilderClassName) builderClassName = builderClassName.replace("*", td.name.toString()); - replaceNameInBuilderClassName = false; + job.builderClassName = job.replaceBuilderClassName(td.name); + if (!checkName("builderClassName", job.builderClassName, annotationNode)) return; } else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) { JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); if (!jmd.typarams.isEmpty()) { @@ -240,38 +305,35 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { return; } - tdParent = parent.up(); - JCClassDecl td = (JCClassDecl) tdParent.get(); - returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), tdParent, td.typarams); - typeParams = td.typarams; - thrownExceptions = jmd.thrown; + job.parentType = parent.up(); + JCClassDecl td = (JCClassDecl) job.parentType.get(); + job.typeParams = job.builderTypeParams = td.typarams; + buildMethodReturnType = job.createBuilderParentTypeReference(); + buildMethodThrownExceptions = jmd.thrown; nameOfBuilderMethod = null; - if (replaceNameInBuilderClassName) builderClassName = builderClassName.replace("*", td.name.toString()); - replaceNameInBuilderClassName = false; + job.builderClassName = job.replaceBuilderClassName(td.name); + if (!checkName("builderClassName", job.builderClassName, annotationNode)) return; } else if (fillParametersFrom != null) { - tdParent = parent.up(); - JCClassDecl td = (JCClassDecl) tdParent.get(); + job.parentType = parent.up(); + JCClassDecl td = (JCClassDecl) job.parentType.get(); JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); - isStatic = (jmd.mods.flags & Flags.STATIC) != 0; + job.isStatic = (jmd.mods.flags & Flags.STATIC) != 0; + JCExpression fullReturnType = jmd.restype; - returnType = fullReturnType; - typeParams = jmd.typarams; - thrownExceptions = jmd.thrown; + buildMethodReturnType = fullReturnType; + job.typeParams = job.builderTypeParams = jmd.typarams; + buildMethodThrownExceptions = jmd.thrown; nameOfBuilderMethod = jmd.name; - if (returnType instanceof JCTypeApply) { - returnType = cloneType(tdParent.getTreeMaker(), returnType, ast, annotationNode.getContext()); + if (buildMethodReturnType instanceof JCTypeApply) { + buildMethodReturnType = cloneType(job.getTreeMaker(), buildMethodReturnType, annotationNode); } - if (replaceNameInBuilderClassName) { - String replStr = returnTypeToBuilderClassName(annotationNode, td, returnType, typeParams); - if (replStr == null) - return; - builderClassName = builderClassName.replace("*", replStr); - replaceNameInBuilderClassName = false; + if (job.builderClassName.indexOf('*') > -1) { + String replStr = returnTypeToBuilderClassName(annotationNode, td, buildMethodReturnType, job.typeParams); + if (replStr == null) return; // shuold not happen + job.builderClassName = job.builderClassName.replace("*", replStr); } - if (replaceNameInBuilderClassName) builderClassName = builderClassName.replace("*", td.name.toString()); - if (toBuilder) { - final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type."; - if (returnType instanceof JCArrayTypeTree) { + if (job.toBuilder) { + if (fullReturnType instanceof JCArrayTypeTree) { annotationNode.addError(TO_BUILDER_NOT_SUPPORTED); return; } @@ -284,8 +346,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { tpOnRet = ((JCTypeApply) fullReturnType).arguments; } - JCExpression namingType = returnType; - if (returnType instanceof JCTypeApply) namingType = ((JCTypeApply) returnType).clazz; + JCExpression namingType = fullReturnType; + if (buildMethodReturnType instanceof JCTypeApply) namingType = ((JCTypeApply) buildMethodReturnType).clazz; if (namingType instanceof JCIdent) { simpleName = ((JCIdent) namingType).name; @@ -309,13 +371,13 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { return; } - if (!tdParent.getName().contentEquals(simpleName)) { + if (!job.parentType.getName().contentEquals(simpleName)) { annotationNode.addError(TO_BUILDER_NOT_SUPPORTED); return; } List<JCTypeParameter> tpOnMethod = jmd.typarams; - List<JCTypeParameter> tpOnType = ((JCClassDecl) tdParent.get()).typarams; + List<JCTypeParameter> tpOnType = ((JCClassDecl) job.parentType.get()).typarams; typeArgsForToBuilder = new ArrayList<Name>(); for (JCTypeParameter tp : tpOnMethod) { @@ -336,7 +398,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } } } else { - annotationNode.addError("@Builder is only supported on types, constructors, and methods."); + annotationNode.addError(BUILDER_NODE_NOT_SUPPORTED_ERR); return; } @@ -351,41 +413,41 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { bfd.rawName = raw.name; bfd.annotations = findCopyableAnnotations(param); bfd.type = raw.vartype; - bfd.singularData = getSingularData(param, builderInstance.setterPrefix()); + bfd.singularData = getSingularData(param, annInstance.setterPrefix()); bfd.originalFieldNode = param; addObtainVia(bfd, param); - builderFields.add(bfd); + job.builderFields.add(bfd); } } - JavacNode builderType = findInnerClass(tdParent, builderClassName); - if (builderType == null) { - builderType = makeBuilderClass(isStatic, annotationNode, tdParent, builderClassName, typeParams, ast, accessForOuters); - recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); + job.builderType = findInnerClass(job.parentType, job.builderClassName); + if (job.builderType == null) { + job.builderType = makeBuilderClass(job); + recursiveSetGeneratedBy(job.builderType.get(), annotationNode); } else { - JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get(); - if (isStatic && !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) { + JCClassDecl builderTypeDeclaration = (JCClassDecl) job.builderType.get(); + if (job.isStatic && !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) { annotationNode.addError("Existing Builder must be a static inner class."); return; - } else if (!isStatic && builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) { + } else if (!job.isStatic && builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) { annotationNode.addError("Existing Builder must be a non-static inner class."); return; } - sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); + sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(job.builderType, annotationNode); /* generate errors for @Singular BFDs that have one already defined node. */ { - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { SingularData sd = bfd.singularData; if (sd == null) continue; JavacSingularizer singularizer = sd.getSingularizer(); if (singularizer == null) continue; - if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) { + if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderType, sd)) { bfd.singularData = null; } } } } - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { if (bfd.singularData.getSingularizer().requiresCleaning()) { addCleaning = true; @@ -404,75 +466,75 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } } - generateBuilderFields(builderType, builderFields, ast); + generateBuilderFields(job); if (addCleaning) { - JavacTreeMaker maker = builderType.getTreeMaker(); - JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null); - injectFieldAndMarkGenerated(builderType, uncleanField); - recursiveSetGeneratedBy(uncleanField, ast, annotationNode.getContext()); + JavacTreeMaker maker = job.getTreeMaker(); + JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), job.builderType.toName(CLEAN_FIELD_NAME), maker.TypeIdent(CTC_BOOLEAN), null); + injectFieldAndMarkGenerated(job.builderType, uncleanField); + recursiveSetGeneratedBy(uncleanField, annotationNode); } - if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), false, annotationNode); - if (cd != null) injectMethod(builderType, cd); + if (constructorExists(job.builderType) == MemberExistsResult.NOT_EXISTS) { + JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), job.builderType, List.<JavacNode>nil(), false, annotationNode); + if (cd != null) injectMethod(job.builderType, cd); } - for (BuilderFieldData bfd : builderFields) { - makePrefixedSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, fluent, chain, accessForInners, builderInstance.setterPrefix()); + for (BuilderFieldData bfd : job.builderFields) { + makePrefixedSetterMethodsForBuilder(job, bfd, annInstance.setterPrefix()); } { - MemberExistsResult methodExists = methodExists(buildMethodName, builderType, -1); - if (methodExists == MemberExistsResult.EXISTS_BY_LOMBOK) methodExists = methodExists(buildMethodName, builderType, 0); + MemberExistsResult methodExists = methodExists(job.buildMethodName, job.builderType, -1); + if (methodExists == MemberExistsResult.EXISTS_BY_LOMBOK) methodExists = methodExists(job.buildMethodName, job.builderType, 0); if (methodExists == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl md = generateBuildMethod(cfv, tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning, accessForInners); + JCMethodDecl md = generateBuildMethod(job, nameOfBuilderMethod, buildMethodReturnType, buildMethodThrownExceptions, addCleaning); if (md != null) { - injectMethod(builderType, md); - recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); + recursiveSetGeneratedBy(md, annotationNode); + injectMethod(job.builderType, md); } } } - if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { + if (methodExists("toString", job.builderType, 0) == MemberExistsResult.NOT_EXISTS) { java.util.List<Included<JavacNode, ToString.Include>> fieldNodes = new ArrayList<Included<JavacNode, ToString.Include>>(); - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { for (JavacNode f : bfd.createdFields) { fieldNodes.add(new Included<JavacNode, ToString.Include>(f, null, true, false)); } } - JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast); - if (md != null) injectMethod(builderType, md); + JCMethodDecl md = HandleToString.createToString(job.builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, job.sourceNode); + if (md != null) injectMethod(job.builderType, md); } - if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); + if (addCleaning) injectMethod(job.builderType, generateCleanMethod(job)); - if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; + if (generateBuilderMethod && methodExists(job.builderMethodName, job.parentType, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; if (generateBuilderMethod) { - JCMethodDecl md = generateBuilderMethod(cfv, isStatic, builderMethodName, builderClassName, annotationNode, tdParent, typeParams, accessForOuters); - recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); - if (md != null) injectMethod(tdParent, md); + JCMethodDecl md = generateBuilderMethod(job); + recursiveSetGeneratedBy(md, annotationNode); + if (md != null) injectMethod(job.parentType, md); } - if (toBuilder) { - switch (methodExists(toBuilderMethodName, tdParent, 0)) { + if (job.toBuilder) { + switch (methodExists(TO_BUILDER_METHOD_NAME, job.parentType, 0)) { case EXISTS_BY_USER: annotationNode.addWarning("Not generating toBuilder() as it already exists."); return; case NOT_EXISTS: - List<JCTypeParameter> tps = typeParams; + List<JCTypeParameter> tps = job.typeParams; if (typeArgsForToBuilder != null) { ListBuffer<JCTypeParameter> lb = new ListBuffer<JCTypeParameter>(); - JavacTreeMaker maker = tdParent.getTreeMaker(); + JavacTreeMaker maker = job.getTreeMaker(); for (Name n : typeArgsForToBuilder) { lb.append(maker.TypeParameter(n, List.<JCExpression>nil())); } tps = lb.toList(); } - JCMethodDecl md = generateToBuilderMethod(cfv, toBuilderMethodName, builderClassName, tdParent, isStatic, tps, builderFields, fluent, ast, accessForOuters, builderInstance.setterPrefix()); + JCMethodDecl md = generateToBuilderMethod(job, tps, annInstance.setterPrefix()); if (md != null) { - recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); - injectMethod(tdParent, md); + recursiveSetGeneratedBy(md, annotationNode); + injectMethod(job.parentType, md); } } } @@ -553,47 +615,41 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { sb.append("__ERR__"); } - private static final String BUILDER_TEMP_VAR = "builder"; - private JCMethodDecl generateToBuilderMethod(CheckerFrameworkVersion cfv, String toBuilderMethodName, String builderClassName, JavacNode type, boolean isStatic, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, boolean fluent, JCAnnotation ast, AccessLevel access, String prefix) { + private JCMethodDecl generateToBuilderMethod(BuilderJob job, List<JCTypeParameter> typeParameters, String prefix) { // return new ThingieBuilder<A, B>().setA(this.a).setB(this.b); - JavacTreeMaker maker = type.getTreeMaker(); + JavacTreeMaker maker = job.getTreeMaker(); - ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); - for (JCTypeParameter typeParam : typeParams) { - typeArgs.append(maker.Ident(typeParam.name)); - } - - JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), !isStatic, typeParams), List.<JCExpression>nil(), null); + JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, job.parentType, job.toName(job.builderClassName), !job.isStatic, job.builderTypeParams), List.<JCExpression>nil(), null); JCExpression invoke = call; ListBuffer<JCStatement> preStatements = null; ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); - for (BuilderFieldData bfd : builderFields) { - String setterPrefix = !prefix.isEmpty() ? prefix : fluent ? "" : "set"; + for (BuilderFieldData bfd : job.builderFields) { + String setterPrefix = !prefix.isEmpty() ? prefix : job.oldFluent ? "" : "set"; String prefixedSetterName = bfd.name.toString(); if (!setterPrefix.isEmpty()) prefixedSetterName = HandlerUtil.buildAccessorName(setterPrefix, prefixedSetterName); - Name setterName = type.toName(prefixedSetterName); + Name setterName = job.toName(prefixedSetterName); JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2]; if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { for (int i = 0; i < tgt.length; i++) { - tgt[i] = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field())); + tgt[i] = maker.Select(maker.Ident(job.toName("this")), bfd.obtainVia == null ? bfd.rawName : job.toName(bfd.obtainVia.field())); } } else { String name = bfd.obtainVia.method(); JCMethodInvocation inv; if (bfd.obtainVia.isStatic()) { - JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(name)); - inv = maker.Apply(typeParameterNames(maker, typeParams), c, List.<JCExpression>of(maker.Ident(type.toName("this")))); + JCExpression c = maker.Select(maker.Ident(job.toName(job.parentType.getName())), job.toName(name)); + inv = maker.Apply(typeParameterNames(maker, typeParameters), c, List.<JCExpression>of(maker.Ident(job.toName("this")))); } else { - JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(name)); + JCExpression c = maker.Select(maker.Ident(job.toName("this")), job.toName(name)); inv = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil()); } for (int i = 0; i < tgt.length; i++) tgt[i] = maker.Ident(bfd.name); // javac appears to cache the type of JCMethodInvocation expressions based on position, meaning, if you have 2 ObtainVia-based method invokes on different types, you get bizarre type mismatch errors. // going via a local variable declaration solves the problem. - JCExpression varType = JavacHandlerUtil.cloneType(maker, bfd.type, ast, type.getContext()); + JCExpression varType = JavacHandlerUtil.cloneType(maker, bfd.type, job.sourceNode); if (preStatements == null) preStatements = new ListBuffer<JCStatement>(); preStatements.append(maker.VarDef(maker.Modifiers(Flags.FINAL), bfd.name, varType, inv)); } @@ -604,15 +660,15 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { invoke = maker.Apply(List.<JCExpression>nil(), maker.Select(invoke, setterName), List.of(arg)); } else { JCExpression isNotNull = maker.Binary(CTC_NOT_EQUAL, tgt[0], maker.Literal(CTC_BOT, null)); - JCExpression invokeBuilder = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName(BUILDER_TEMP_VAR)), setterName), List.<JCExpression>of(tgt[1])); + JCExpression invokeBuilder = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(job.toName(BUILDER_TEMP_VAR)), setterName), List.<JCExpression>of(tgt[1])); statements.append(maker.If(isNotNull, maker.Exec(invokeBuilder), null)); } } if (!statements.isEmpty()) { - JCExpression tempVarType = namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), !isStatic, typeParams); - statements.prepend(maker.VarDef(maker.Modifiers(Flags.FINAL), type.toName(BUILDER_TEMP_VAR), tempVarType, invoke)); - statements.append(maker.Return(maker.Ident(type.toName(BUILDER_TEMP_VAR)))); + JCExpression tempVarType = namePlusTypeParamsToTypeReference(maker, job.parentType, job.getBuilderClassName(), !job.isStatic, typeParameters); + statements.prepend(maker.VarDef(maker.Modifiers(Flags.FINAL), job.toName(BUILDER_TEMP_VAR), tempVarType, invoke)); + statements.append(maker.Return(maker.Ident(job.toName(BUILDER_TEMP_VAR)))); } else { statements.append(maker.Return(invoke)); } @@ -622,77 +678,78 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { statements = preStatements; } JCBlock body = maker.Block(0, statements.toList()); - List<JCAnnotation> annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__UNIQUE), List.<JCExpression>nil())) : List.<JCAnnotation>nil(); - JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(toJavacModifier(access), annsOnMethod), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), !isStatic, typeParams), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); - createRelevantNonNullAnnotation(type, methodDef); + List<JCAnnotation> annsOnParamType = List.nil(); + if (job.checkerFramework.generateUnique()) annsOnParamType = List.of(maker.Annotation(genTypeRef(job.parentType, CheckerFrameworkVersion.NAME__UNIQUE), List.<JCExpression>nil())); + JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(toJavacModifier(job.accessOuters)), job.toName(TO_BUILDER_METHOD_NAME), namePlusTypeParamsToTypeReference(maker, job.parentType, job.getBuilderClassName(), !job.isStatic, typeParameters, annsOnParamType), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); + createRelevantNonNullAnnotation(job.parentType, methodDef); return methodDef; } - private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) { - JavacTreeMaker maker = type.getTreeMaker(); + private JCMethodDecl generateCleanMethod(BuilderJob job) { + JavacTreeMaker maker = job.getTreeMaker(); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements); + bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, job.builderType, job.sourceNode, statements); } } - statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0)))); + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(job.toName("this")), job.toName(CLEAN_FIELD_NAME)), maker.Literal(CTC_BOOLEAN, 0)))); JCBlock body = maker.Block(0, statements.toList()); - JCMethodDecl method = maker.MethodDef(maker.Modifiers(toJavacModifier(AccessLevel.PRIVATE)), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(type.getSymbolTable(), CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); - recursiveSetGeneratedBy(method, source, type.getContext()); + JCMethodDecl method = maker.MethodDef(maker.Modifiers(toJavacModifier(AccessLevel.PRIVATE)), job.toName(CLEAN_METHOD_NAME), maker.Type(Javac.createVoidType(job.builderType.getSymbolTable(), CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); + recursiveSetGeneratedBy(method, job.sourceNode); return method; } - static List<JCVariableDecl> generateBuildArgs(CheckerFrameworkVersion cfv, JavacNode type, java.util.List<BuilderFieldData> builderFields) { - if (!cfv.generateCalledMethods()) return List.<JCVariableDecl>nil(); - + static JCVariableDecl generateReceiver(BuilderJob job) { + if (!job.checkerFramework.generateCalledMethods()) return null; + ArrayList<String> mandatories = new ArrayList<String>(); - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData == null && bfd.nameOfSetFlag == null) mandatories.add(bfd.name.toString()); } - + JCExpression arg; - JavacTreeMaker maker = type.getTreeMaker(); - if (mandatories.size() == 0) return List.<JCVariableDecl>nil(); + JavacTreeMaker maker = job.getTreeMaker(); + if (mandatories.size() == 0) return null; if (mandatories.size() == 1) arg = maker.Literal(mandatories.get(0)); else { List<JCExpression> elems = List.nil(); for (int i = mandatories.size() - 1; i >= 0; i--) elems = elems.prepend(maker.Literal(mandatories.get(i))); arg = maker.NewArray(null, List.<JCExpression>nil(), elems); } - JCAnnotation recvAnno = maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__CALLED), List.of(arg)); - JCClassDecl builderTypeNode = (JCClassDecl) type.get(); - JCVariableDecl recv = maker.VarDef(maker.Modifiers(0L, List.<JCAnnotation>of(recvAnno)), type.toName("this"), namePlusTypeParamsToTypeReference(maker, type, builderTypeNode.typarams), null); - return List.of(recv); + JCAnnotation recvAnno = maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__CALLED), List.of(arg)); + JCClassDecl builderTypeNode = (JCClassDecl) job.builderType.get(); + JCVariableDecl recv = maker.VarDef(maker.Modifiers(Flags.PARAMETER, List.<JCAnnotation>nil()), job.toName("this"), namePlusTypeParamsToTypeReference(maker, job.builderType, builderTypeNode.typarams, List.<JCAnnotation>of(recvAnno)), null); + return recv; } - private JCMethodDecl generateBuildMethod(CheckerFrameworkVersion cfv, JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning, AccessLevel access) { - JavacTreeMaker maker = type.getTreeMaker(); + private JCMethodDecl generateBuildMethod(BuilderJob job, Name staticName, JCExpression returnType, List<JCExpression> thrownExceptions, boolean addCleaning) { + JavacTreeMaker maker = job.getTreeMaker(); JCExpression call; ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); if (addCleaning) { - JCExpression notClean = maker.Unary(CTC_NOT, maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean"))); - JCStatement invokeClean = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(type.toName("$lombokClean")), List.<JCExpression>nil())); + JCExpression notClean = maker.Unary(CTC_NOT, maker.Select(maker.Ident(job.toName("this")), job.toName(CLEAN_FIELD_NAME))); + JCStatement invokeClean = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(job.toName(CLEAN_METHOD_NAME)), List.<JCExpression>nil())); JCIf ifUnclean = maker.If(notClean, invokeClean, null); statements.append(ifUnclean); } - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.builderFieldName, "this"); + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, job.builderType, job.sourceNode, statements, bfd.builderFieldName, "this"); } } ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); - Name thisName = type.toName("this"); - for (BuilderFieldData bfd : builderFields) { + Name thisName = job.toName("this"); + for (BuilderFieldData bfd : job.builderFields) { if (bfd.nameOfSetFlag != null) { - statements.append(maker.VarDef(maker.Modifiers(0L), bfd.builderFieldName, cloneType(maker, bfd.type, source, tdParent.getContext()), maker.Select(maker.Ident(thisName), bfd.builderFieldName))); - statements.append(maker.If(maker.Unary(CTC_NOT, maker.Select(maker.Ident(thisName), bfd.nameOfSetFlag)), maker.Exec(maker.Assign(maker.Ident(bfd.builderFieldName), maker.Apply(typeParameterNames(maker, ((JCClassDecl) tdParent.get()).typarams), maker.Select(maker.Ident(((JCClassDecl) tdParent.get()).name), bfd.nameOfDefaultProvider), List.<JCExpression>nil()))), null)); + statements.append(maker.VarDef(maker.Modifiers(0L), bfd.builderFieldName, cloneType(maker, bfd.type, job.sourceNode), maker.Select(maker.Ident(thisName), bfd.builderFieldName))); + statements.append(maker.If(maker.Unary(CTC_NOT, maker.Select(maker.Ident(thisName), bfd.nameOfSetFlag)), maker.Exec(maker.Assign(maker.Ident(bfd.builderFieldName), maker.Apply(typeParameterNames(maker, ((JCClassDecl) job.parentType.get()).typarams), maker.Select(maker.Ident(((JCClassDecl) job.parentType.get()).name), bfd.nameOfDefaultProvider), List.<JCExpression>nil()))), null)); } if (bfd.nameOfSetFlag != null || (bfd.singularData != null && bfd.singularData.getSingularizer().shadowedDuringBuild())) { args.append(maker.Ident(bfd.builderFieldName)); @@ -702,20 +759,20 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } if (addCleaning) { - statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 1)))); + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(job.toName("this")), job.toName(CLEAN_FIELD_NAME)), maker.Literal(CTC_BOOLEAN, 1)))); } - if (builderName == null) { + if (staticName == null) { call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null); statements.append(maker.Return(call)); } else { ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>(); - for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) { + for (JCTypeParameter tp : ((JCClassDecl) job.builderType.get()).typarams) { typeParams.append(maker.Ident(tp.name)); } - JCExpression callee = maker.Ident(((JCClassDecl) type.up().get()).name); - if (!isStatic) callee = maker.Select(callee, type.up().toName("this")); - JCExpression fn = maker.Select(callee, builderName); + JCExpression callee = maker.Ident(((JCClassDecl) job.parentType.get()).name); + if (!job.isStatic) callee = maker.Select(callee, job.toName("this")); + JCExpression fn = maker.Select(callee, staticName); call = maker.Apply(typeParams.toList(), fn, args.toList()); if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) { statements.append(maker.Exec(call)); @@ -726,10 +783,15 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JCBlock body = maker.Block(0, statements.toList()); - List<JCAnnotation> annsOnMethod = cfv.generateSideEffectFree() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil(); - List<JCVariableDecl> params = generateBuildArgs(cfv, type, builderFields); - JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(toJavacModifier(access), annsOnMethod), type.toName(buildName), returnType, List.<JCTypeParameter>nil(), params, thrownExceptions, body, null); - if (builderName == null) createRelevantNonNullAnnotation(type, methodDef); + List<JCAnnotation> annsOnMethod = job.checkerFramework.generateSideEffectFree() ? List.of(maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil(); + JCVariableDecl recv = generateReceiver(job); + JCMethodDecl methodDef; + if (recv != null && maker.hasMethodDefWithRecvParam()) { + methodDef = maker.MethodDefWithRecvParam(maker.Modifiers(toJavacModifier(job.accessInners), annsOnMethod), job.toName(job.buildMethodName), returnType, List.<JCTypeParameter>nil(), recv, List.<JCVariableDecl>nil(), thrownExceptions, body, null); + } else { + methodDef = maker.MethodDef(maker.Modifiers(toJavacModifier(job.accessInners), annsOnMethod), job.toName(job.buildMethodName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null); + } + if (staticName == null) createRelevantNonNullAnnotation(job.builderType, methodDef); return methodDef; } @@ -742,55 +804,56 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); int modifiers = Flags.PRIVATE | Flags.STATIC; - return maker.MethodDef(maker.Modifiers(modifiers), methodName, cloneType(maker, field.vartype, field, fieldNode.getContext()), copyTypeParams(fieldNode, params), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); + return maker.MethodDef(maker.Modifiers(modifiers), methodName, cloneType(maker, field.vartype, fieldNode), copyTypeParams(fieldNode, params), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); } - public JCMethodDecl generateBuilderMethod(CheckerFrameworkVersion cfv, boolean isStatic, String builderMethodName, String builderClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams, AccessLevel access) { - JavacTreeMaker maker = type.getTreeMaker(); + public JCMethodDecl generateBuilderMethod(BuilderJob job) { + //String builderClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams, AccessLevel access) { + //builderClassName, annotationNode, tdParent, typeParams, accessForOuters); - ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); - for (JCTypeParameter typeParam : typeParams) { - typeArgs.append(maker.Ident(typeParam.name)); - } + JavacTreeMaker maker = job.getTreeMaker(); JCExpression call; - if (isStatic) { - call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), false, typeParams), List.<JCExpression>nil(), null); + if (job.isStatic) { + call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, job.parentType, job.toName(job.builderClassName), false, job.typeParams), List.<JCExpression>nil(), null); } else { - call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, null, type.toName(builderClassName), false, typeParams), List.<JCExpression>nil(), null); - ((JCNewClass) call).encl = maker.Ident(type.toName("this")); + call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, null, job.toName(job.builderClassName), false, job.typeParams), List.<JCExpression>nil(), null); + ((JCNewClass) call).encl = maker.Ident(job.toName("this")); } JCStatement statement = maker.Return(call); JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); - int modifiers = toJavacModifier(access); - if (isStatic) modifiers |= Flags.STATIC; - JCAnnotation annUnique = cfv.generateUnique() ? maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__UNIQUE), List.<JCExpression>nil()) : null; - JCAnnotation annSef = cfv.generateSideEffectFree() ? maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil()) : null; - List<JCAnnotation> annsOnMethod; - if (annUnique != null && annSef != null) annsOnMethod = List.of(annUnique, annSef); - else if (annUnique != null) annsOnMethod = List.of(annUnique); - else if (annSef != null) annsOnMethod = List.of(annSef); - else annsOnMethod = List.nil(); - JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), !isStatic, typeParams), copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); - createRelevantNonNullAnnotation(type, methodDef); + int modifiers = toJavacModifier(job.accessOuters); + if (job.isStatic) modifiers |= Flags.STATIC; + List<JCAnnotation> annsOnMethod = List.nil(); + if (job.checkerFramework.generateSideEffectFree()) annsOnMethod = List.of(maker.Annotation(genTypeRef(job.parentType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())); + List<JCAnnotation> annsOnParamType = List.nil(); + if (job.checkerFramework.generateUnique()) annsOnParamType = List.of(maker.Annotation(genTypeRef(job.parentType, CheckerFrameworkVersion.NAME__UNIQUE), List.<JCExpression>nil())); + + JCExpression returnType = namePlusTypeParamsToTypeReference(maker, job.parentType, job.getBuilderClassName(), !job.isStatic, job.builderTypeParams, annsOnParamType); + JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), job.toName(job.builderMethodName), returnType, job.copyTypeParams(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); + createRelevantNonNullAnnotation(job.parentType, methodDef); return methodDef; } - public void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) { - int len = builderFields.size(); + public void generateBuilderFields(BuilderJob job) { + int len = job.builderFields.size(); java.util.List<JavacNode> existing = new ArrayList<JavacNode>(); - for (JavacNode child : builderType.down()) { + for (JavacNode child : job.builderType.down()) { if (child.getKind() == Kind.FIELD) existing.add(child); } java.util.List<JCVariableDecl> generated = new ArrayList<JCVariableDecl>(); for (int i = len - 1; i >= 0; i--) { - BuilderFieldData bfd = builderFields.get(i); + BuilderFieldData bfd = job.builderFields.get(i); if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source)); + java.util.List<JavacNode> generateSingularFields = bfd.singularData.getSingularizer().generateFields(bfd.singularData, job.builderType, job.sourceNode); + for (JavacNode field : generateSingularFields) { + generated.add((JCVariableDecl) field.get()); + } + bfd.createdFields.addAll(generateSingularFields); } else { JavacNode field = null, setFlag = null; for (JavacNode exists : existing) { @@ -798,40 +861,41 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (n.equals(bfd.builderFieldName)) field = exists; if (n.equals(bfd.nameOfSetFlag)) setFlag = exists; } - JavacTreeMaker maker = builderType.getTreeMaker(); + JavacTreeMaker maker = job.getTreeMaker(); if (field == null) { JCModifiers mods = maker.Modifiers(Flags.PRIVATE); - JCVariableDecl newField = maker.VarDef(mods, bfd.builderFieldName, cloneType(maker, bfd.type, source, builderType.getContext()), null); - field = injectFieldAndMarkGenerated(builderType, newField); + JCVariableDecl newField = maker.VarDef(mods, bfd.builderFieldName, cloneType(maker, bfd.type, job.sourceNode), null); + field = injectFieldAndMarkGenerated(job.builderType, newField); generated.add(newField); } if (setFlag == null && bfd.nameOfSetFlag != null) { JCModifiers mods = maker.Modifiers(Flags.PRIVATE); JCVariableDecl newField = maker.VarDef(mods, bfd.nameOfSetFlag, maker.TypeIdent(CTC_BOOLEAN), null); - injectFieldAndMarkGenerated(builderType, newField); + injectFieldAndMarkGenerated(job.builderType, newField); generated.add(newField); } bfd.createdFields.add(field); } } - for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, source, builderType.getContext()); + for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, job.sourceNode); } - public void makePrefixedSetterMethodsForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain, AccessLevel access, String prefix) { - boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); - if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - makePrefixedSetterMethodForBuilder(cfv, builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, source, fluent, chain, fieldNode.annotations, fieldNode.originalFieldNode, access, prefix); + public void makePrefixedSetterMethodsForBuilder(BuilderJob job, BuilderFieldData bfd, String prefix) { + boolean deprecate = isFieldDeprecated(bfd.originalFieldNode); + if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { + makePrefixedSetterMethodForBuilder(job, bfd, deprecate, prefix); } else { - fieldNode.singularData.getSingularizer().generateMethods(cfv, fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain, access); + bfd.singularData.getSingularizer().generateMethods(job, bfd.singularData, deprecate); } } - private void makePrefixedSetterMethodForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, List<JCAnnotation> annosOnParam, JavacNode originalFieldNode, AccessLevel access, String prefix) { - String setterPrefix = !prefix.isEmpty() ? prefix : fluent ? "" : "set"; - String setterName = HandlerUtil.buildAccessorName(setterPrefix, paramName.toString()); - Name setterName_ = builderType.toName(setterName); + private void makePrefixedSetterMethodForBuilder(BuilderJob job, BuilderFieldData bfd, boolean deprecate, String prefix) { + JavacNode fieldNode = bfd.createdFields.get(0); + String setterPrefix = !prefix.isEmpty() ? prefix : job.oldFluent ? "" : "set"; + String setterName = HandlerUtil.buildAccessorName(setterPrefix, bfd.name.toString()); + Name setterName_ = job.builderType.toName(setterName); - for (JavacNode child : builderType.down()) { + for (JavacNode child : job.builderType.down()) { if (child.getKind() != Kind.METHOD) continue; JCMethodDecl methodDecl = (JCMethodDecl) child.get(); Name existingName = methodDecl.name; @@ -840,47 +904,45 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JavacTreeMaker maker = fieldNode.getTreeMaker(); - List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); - JCMethodDecl newMethod = HandleSetter.createSetter(toJavacModifier(access), deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, chain, source, methodAnns, annosOnParam); - if (cfv.generateCalledMethods()) { - JCAnnotation ncAnno = maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__NOT_CALLED), List.<JCExpression>of(maker.Literal(newMethod.getName().toString()))); - JCClassDecl builderTypeNode = (JCClassDecl) builderType.get(); - JCExpression selfType = namePlusTypeParamsToTypeReference(maker, builderType, builderTypeNode.typarams); - JCVariableDecl recv = maker.VarDef(maker.Modifiers(0L, List.<JCAnnotation>of(ncAnno)), builderType.toName("this"), selfType, null); - newMethod.params = List.of(recv, newMethod.params.get(0)); - } - recursiveSetGeneratedBy(newMethod, source.get(), builderType.getContext()); - if (source.up().getKind() == Kind.METHOD) { - copyJavadocFromParam(originalFieldNode.up(), newMethod, paramName.toString()); + List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(bfd.originalFieldNode); + JCMethodDecl newMethod = null; + if (job.checkerFramework.generateCalledMethods() && maker.hasMethodDefWithRecvParam()) { + JCAnnotation ncAnno = maker.Annotation(genTypeRef(job.sourceNode, CheckerFrameworkVersion.NAME__NOT_CALLED), List.<JCExpression>of(maker.Literal(setterName.toString()))); + JCClassDecl builderTypeNode = (JCClassDecl) job.builderType.get(); + JCExpression selfType = namePlusTypeParamsToTypeReference(maker, job.builderType, builderTypeNode.typarams, List.<JCAnnotation>of(ncAnno)); + JCVariableDecl recv = maker.VarDef(maker.Modifiers(Flags.PARAMETER, List.<JCAnnotation>nil()), job.builderType.toName("this"), selfType, null); + newMethod = HandleSetter.createSetterWithRecv(toJavacModifier(job.accessInners), deprecate, fieldNode, maker, setterName, bfd.name, bfd.nameOfSetFlag, job.oldChain, job.sourceNode, methodAnns, bfd.annotations, recv); + } + if (newMethod == null) newMethod = HandleSetter.createSetter(toJavacModifier(job.accessInners), deprecate, fieldNode, maker, setterName, bfd.name, bfd.nameOfSetFlag, job.oldChain, job.sourceNode, methodAnns, bfd.annotations); + recursiveSetGeneratedBy(newMethod, job.sourceNode); + if (job.sourceNode.up().getKind() == Kind.METHOD) { + copyJavadocFromParam(bfd.originalFieldNode.up(), newMethod, bfd.name.toString()); } else { - copyJavadoc(originalFieldNode, newMethod, CopyJavadoc.SETTER, true); + copyJavadoc(bfd.originalFieldNode, newMethod, CopyJavadoc.SETTER, true); } - injectMethod(builderType, newMethod); + injectMethod(job.builderType, newMethod); } private void copyJavadocFromParam(JavacNode from, JCMethodDecl to, String param) { try { JCCompilationUnit cu = ((JCCompilationUnit) from.top().get()); String methodComment = Javac.getDocComment(cu, 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()); - Javac.setDocComment(cu, to, newJavadoc); - } + String newJavadoc = addReturnsThisIfNeeded(getParamJavadoc(methodComment, param)); + Javac.setDocComment(cu, to, newJavadoc); } catch (Exception ignore) {} - } - - public JavacNode makeBuilderClass(boolean isStatic, JavacNode source, JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast, AccessLevel access) { - JavacTreeMaker maker = tdParent.getTreeMaker(); - int modifiers = toJavacModifier(access); - if (isStatic) modifiers |= Flags.STATIC; + } + + public JavacNode makeBuilderClass(BuilderJob job) { + //boolean isStatic, JavacNode source, JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast, AccessLevel access) { + //isStatic, annotationNode, tdParent, builderClassName, typeParams, ast, accessForOuters + JavacTreeMaker maker = job.getTreeMaker(); + int modifiers = toJavacModifier(job.accessOuters); + if (job.isStatic) modifiers |= Flags.STATIC; JCModifiers mods = maker.Modifiers(modifiers); - JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(source, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil()); - return injectType(tdParent, builder); + JCClassDecl builder = maker.ClassDef(mods, job.getBuilderClassName(), job.copyTypeParams(), null, List.<JCExpression>nil(), List.<JCTree>nil()); + recursiveSetGeneratedBy(builder, job.sourceNode); + return injectType(job.parentType, builder); } private void addObtainVia(BuilderFieldData bfd, JavacNode node) { diff --git a/src/core/lombok/javac/handlers/HandleBuilderDefault.java b/src/core/lombok/javac/handlers/HandleBuilderDefault.java index df5eebc9..8438057a 100644 --- a/src/core/lombok/javac/handlers/HandleBuilderDefault.java +++ b/src/core/lombok/javac/handlers/HandleBuilderDefault.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2018 The Project Lombok Authors. + * Copyright (C) 2017-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,8 +23,6 @@ package lombok.javac.handlers; import static lombok.javac.handlers.JavacHandlerUtil.*; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCIdent; @@ -36,8 +34,9 @@ import lombok.core.HandlerPriority; import lombok.experimental.SuperBuilder; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; +import lombok.spi.Provides; -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(-1025) //HandleBuilder's level, minus one. public class HandleBuilderDefault extends JavacAnnotationHandler<Builder.Default> { @Override public void handle(AnnotationValues<Builder.Default> annotation, JCAnnotation ast, JavacNode annotationNode) { @@ -55,6 +54,7 @@ public class HandleBuilderDefault extends JavacAnnotationHandler<Builder.Default JCFieldAccess jfa = (JCFieldAccess) ast.annotationType; if (jfa.selected instanceof JCIdent && ((JCIdent) jfa.selected).name.contentEquals("Builder") && jfa.name.contentEquals("Default")) { JCFieldAccess newJfaSel = annotationNode.getTreeMaker().Select(annotationNode.getTreeMaker().Ident(annotationNode.toName("lombok")), ((JCIdent) jfa.selected).name); + recursiveSetGeneratedBy(newJfaSel, annotationNode); jfa.selected = newJfaSel; } } diff --git a/src/core/lombok/javac/handlers/HandleBuilderDefaultRemove.java b/src/core/lombok/javac/handlers/HandleBuilderDefaultRemove.java index fc0738c3..d7bc28cb 100644 --- a/src/core/lombok/javac/handlers/HandleBuilderDefaultRemove.java +++ b/src/core/lombok/javac/handlers/HandleBuilderDefaultRemove.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Project Lombok Authors. + * Copyright (C) 2018-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,8 +23,6 @@ package lombok.javac.handlers; import static lombok.javac.handlers.JavacHandlerUtil.*; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.tree.JCTree.JCAnnotation; import lombok.Builder; @@ -34,9 +32,10 @@ import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; +import lombok.spi.Provides; -@ProviderFor(JavacAnnotationHandler.class) -@HandlerPriority(65536) +@Provides +@HandlerPriority(32768) @AlreadyHandledAnnotations public class HandleBuilderDefaultRemove extends JavacAnnotationHandler<Builder.Default> { @Override public void handle(AnnotationValues<Default> annotation, JCAnnotation ast, JavacNode annotationNode) { diff --git a/src/core/lombok/javac/handlers/HandleBuilderRemove.java b/src/core/lombok/javac/handlers/HandleBuilderRemove.java index 6e59b40f..a47ed6b0 100644 --- a/src/core/lombok/javac/handlers/HandleBuilderRemove.java +++ b/src/core/lombok/javac/handlers/HandleBuilderRemove.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Project Lombok Authors. + * Copyright (C) 2020-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,8 +23,6 @@ package lombok.javac.handlers; import static lombok.javac.handlers.JavacHandlerUtil.*; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.tree.JCTree.JCAnnotation; import lombok.Builder; @@ -33,9 +31,10 @@ import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; +import lombok.spi.Provides; -@ProviderFor(JavacAnnotationHandler.class) -@HandlerPriority(65536) +@Provides +@HandlerPriority(32768) @AlreadyHandledAnnotations public class HandleBuilderRemove extends JavacAnnotationHandler<Builder> { @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) { diff --git a/src/core/lombok/javac/handlers/HandleCleanup.java b/src/core/lombok/javac/handlers/HandleCleanup.java index 4aa61764..785ebf8a 100644 --- a/src/core/lombok/javac/handlers/HandleCleanup.java +++ b/src/core/lombok/javac/handlers/HandleCleanup.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,8 +32,7 @@ import lombok.delombok.LombokOptionsFactory; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; @@ -52,7 +51,6 @@ import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeCast; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; -import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; @@ -60,7 +58,7 @@ import com.sun.tools.javac.util.Name; /** * Handles the {@code lombok.Cleanup} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleCleanup extends JavacAnnotationHandler<Cleanup> { @Override public void handle(AnnotationValues<Cleanup> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.CLEANUP_FLAG_USAGE, "@Cleanup"); @@ -129,10 +127,9 @@ public class HandleCleanup extends JavacAnnotationHandler<Cleanup> { JCIf ifNotNullCleanup = maker.If(isNull, maker.Block(0, cleanupCall), null); - Context context = annotationNode.getContext(); - JCBlock finalizer = recursiveSetGeneratedBy(maker.Block(0, List.<JCStatement>of(ifNotNullCleanup)), ast, context); + JCBlock finalizer = recursiveSetGeneratedBy(maker.Block(0, List.<JCStatement>of(ifNotNullCleanup)), annotationNode); - newStatements.append(setGeneratedBy(maker.Try(setGeneratedBy(maker.Block(0, tryBlock.toList()), ast, context), List.<JCCatch>nil(), finalizer), ast, context)); + newStatements.append(setGeneratedBy(maker.Try(setGeneratedBy(maker.Block(0, tryBlock.toList()), annotationNode), List.<JCCatch>nil(), finalizer), annotationNode)); if (blockNode instanceof JCBlock) { ((JCBlock)blockNode).stats = newStatements.toList(); diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index 54a9f821..9c74ca0e 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2019 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,8 +25,6 @@ import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Type; @@ -55,18 +53,17 @@ import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.LombokNode; import lombok.core.configuration.CheckerFrameworkVersion; import lombok.delombok.LombokOptionsFactory; import lombok.javac.Javac; -import lombok.javac.JavacAST; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult; +import lombok.spi.Provides; public class HandleConstructor { - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleNoArgsConstructor extends JavacAnnotationHandler<NoArgsConstructor> { private static final String NAME = NoArgsConstructor.class.getSimpleName(); private HandleConstructor handleConstructor = new HandleConstructor(); @@ -88,7 +85,7 @@ public class HandleConstructor { } } - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleRequiredArgsConstructor extends JavacAnnotationHandler<RequiredArgsConstructor> { private static final String NAME = RequiredArgsConstructor.class.getSimpleName(); private HandleConstructor handleConstructor = new HandleConstructor(); @@ -138,7 +135,7 @@ public class HandleConstructor { return fields.toList(); } - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleAllArgsConstructor extends JavacAnnotationHandler<AllArgsConstructor> { private static final String NAME = AllArgsConstructor.class.getSimpleName(); private HandleConstructor handleConstructor = new HandleConstructor(); @@ -264,11 +261,11 @@ public class HandleConstructor { generateStaticConstructor(staticConstrRequired, typeNode, staticName, level, allToDefault, fields, source, argTypes_); } - private void generateStaticConstructor(boolean staticConstrRequired, JavacNode typeNode, String staticName, AccessLevel level, boolean allToDefault, List<JavacNode> fields, LombokNode<JavacAST, JavacNode, JCTree> source, List<Type> argTypes_) { + private void generateStaticConstructor(boolean staticConstrRequired, JavacNode typeNode, String staticName, AccessLevel level, boolean allToDefault, List<JavacNode> fields, JavacNode source, List<Type> argTypes_) { if (staticConstrRequired) { ClassSymbol sym = ((JCClassDecl) typeNode.get()).sym; Type returnType = sym == null ? null : sym.type; - JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, allToDefault ? List.<JavacNode>nil() : fields, source.get()); + JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, allToDefault ? List.<JavacNode>nil() : fields, source); injectMethod(typeNode, staticConstr, argTypes_, returnType); } } @@ -337,7 +334,7 @@ public class HandleConstructor { Name rawName = field.name; List<JCAnnotation> copyableAnnotations = findCopyableAnnotations(fieldNode); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); - JCExpression pType = cloneType(fieldNode.getTreeMaker(), field.vartype, source.get(), source.getContext()); + JCExpression pType = cloneType(fieldNode.getTreeMaker(), field.vartype, source); JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, copyableAnnotations), fieldName, pType, null); params.append(param); if (hasNonNullAnnotations(fieldNode)) { @@ -372,10 +369,9 @@ public class HandleConstructor { addConstructorProperties(mods, typeNode, fieldsToParam); } if (onConstructor != null) mods.annotations = mods.annotations.appendList(copyAnnotations(onConstructor)); - if (getCheckerFrameworkVersion(source).generateUnique()) mods.annotations = mods.annotations.prepend(maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__UNIQUE), List.<JCExpression>nil())); return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"), null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), - maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext()); + maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source); } /** @@ -446,12 +442,11 @@ public class HandleConstructor { return true; } - public JCMethodDecl createStaticConstructor(String name, AccessLevel level, JavacNode typeNode, List<JavacNode> fields, JCTree source) { + public JCMethodDecl createStaticConstructor(String name, AccessLevel level, JavacNode typeNode, List<JavacNode> fields, JavacNode source) { JavacTreeMaker maker = typeNode.getTreeMaker(); JCClassDecl type = (JCClassDecl) typeNode.get(); JCModifiers mods = maker.Modifiers(Flags.STATIC | toJavacModifier(level)); - if (getCheckerFrameworkVersion(typeNode).generateUnique()) mods.annotations = mods.annotations.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__UNIQUE), List.<JCExpression>nil())); JCExpression returnType, constructorType; @@ -464,13 +459,15 @@ public class HandleConstructor { typeParams.append(maker.TypeParameter(param.name, param.bounds)); } } - returnType = namePlusTypeParamsToTypeReference(maker, typeNode, type.typarams); + List<JCAnnotation> annsOnReturnType = List.nil(); + if (getCheckerFrameworkVersion(typeNode).generateUnique()) annsOnReturnType = List.of(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__UNIQUE), List.<JCExpression>nil())); + returnType = namePlusTypeParamsToTypeReference(maker, typeNode, type.typarams, annsOnReturnType); constructorType = namePlusTypeParamsToTypeReference(maker, typeNode, type.typarams); for (JavacNode fieldNode : fields) { JCVariableDecl field = (JCVariableDecl) fieldNode.get(); Name fieldName = removePrefixFromField(fieldNode); - JCExpression pType = cloneType(maker, field.vartype, source, typeNode.getContext()); + JCExpression pType = cloneType(maker, field.vartype, source); List<JCAnnotation> copyableAnnotations = findCopyableAnnotations(fieldNode); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, copyableAnnotations), fieldName, pType, null); @@ -482,6 +479,6 @@ public class HandleConstructor { JCMethodDecl methodDef = maker.MethodDef(mods, typeNode.toName(name), returnType, typeParams.toList(), params.toList(), List.<JCExpression>nil(), body, null); createRelevantNonNullAnnotation(typeNode, methodDef); - return recursiveSetGeneratedBy(methodDef, source, typeNode.getContext()); + return recursiveSetGeneratedBy(methodDef, source); } } diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java index 15c9c9e7..06524aa8 100644 --- a/src/core/lombok/javac/handlers/HandleData.java +++ b/src/core/lombok/javac/handlers/HandleData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2018 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,8 +30,7 @@ import lombok.core.AnnotationValues; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.util.List; @@ -39,7 +38,7 @@ import com.sun.tools.javac.util.List; /** * Handles the {@code lombok.Data} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleData extends JavacAnnotationHandler<Data> { private HandleConstructor handleConstructor = new HandleConstructor(); private HandleGetter handleGetter = new HandleGetter(); @@ -52,9 +51,8 @@ public class HandleData extends JavacAnnotationHandler<Data> { deleteAnnotationIfNeccessary(annotationNode, Data.class); JavacNode typeNode = annotationNode.up(); - boolean notAClass = !isClass(typeNode); - if (notAClass) { + if (!isClass(typeNode)) { annotationNode.addError("@Data is only supported on a class."); return; } diff --git a/src/core/lombok/javac/handlers/HandleDelegate.java b/src/core/lombok/javac/handlers/HandleDelegate.java index d6e76ab1..4d72eea3 100644 --- a/src/core/lombok/javac/handlers/HandleDelegate.java +++ b/src/core/lombok/javac/handlers/HandleDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -43,8 +43,6 @@ import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.code.Attribute.Compound; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol; @@ -81,8 +79,10 @@ import lombok.javac.JavacResolution; import lombok.javac.JavacResolution.TypeNotConvertibleException; import lombok.javac.JavacTreeMaker; import lombok.javac.ResolutionResetNeeded; +import lombok.permit.Permit; +import lombok.spi.Provides; -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(HandleDelegate.HANDLE_DELEGATE_PRIORITY) //2^16; to make sure that we also delegate generated methods. @ResolutionResetNeeded public class HandleDelegate extends JavacAnnotationHandler<Delegate> { @@ -348,7 +348,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> { JCStatement body = useReturn ? maker.Return(delegateCall) : maker.Exec(delegateCall); JCBlock bodyBlock = maker.Block(0, com.sun.tools.javac.util.List.of(body)); - return recursiveSetGeneratedBy(maker.MethodDef(mods, sig.name, returnType, toList(typeParams), toList(params), toList(thrown), bodyBlock, null), annotation.get(), annotation.getContext()); + return recursiveSetGeneratedBy(maker.MethodDef(mods, sig.name, returnType, toList(typeParams), toList(params), toList(thrown), bodyBlock, null), annotation); } public static <T> com.sun.tools.javac.util.List<T> toList(ListBuffer<T> collection) { @@ -466,7 +466,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> { static { Method m = null; try { - m = Type.class.getDeclaredMethod("unannotatedType"); + m = Permit.getMethod(Type.class, "unannotatedType"); } catch (Exception e) {/* ignore */} unannotated = m; } @@ -474,7 +474,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> { static Type unannotatedType(Type t) { if (unannotated == null) return t; try { - return (Type) unannotated.invoke(t); + return (Type) Permit.invoke(unannotated, t); } catch (Exception e) { return t; } diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index e35deb5b..ffe882d8 100644 --- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,11 +30,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; import com.sun.tools.javac.tree.JCTree.JCBinary; @@ -42,6 +39,8 @@ import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCModifiers; @@ -56,6 +55,7 @@ import com.sun.tools.javac.util.Name; import lombok.ConfigurationKeys; import lombok.EqualsAndHashCode; +import lombok.EqualsAndHashCode.CacheStrategy; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.configuration.CallSuperType; @@ -68,19 +68,22 @@ import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult; +import lombok.spi.Provides; /** * Handles the {@code lombok.EqualsAndHashCode} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHashCode> { private static final String RESULT_NAME = "result"; private static final String PRIME_NAME = "PRIME"; + private static final String HASH_CODE_CACHE_NAME = "$hashCodeCache"; @Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode"); deleteAnnotationIfNeccessary(annotationNode, EqualsAndHashCode.class); + deleteImportFromCompilationUnit(annotationNode, CacheStrategy.class.getName()); EqualsAndHashCode ann = annotation.getInstance(); java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(annotationNode.up(), annotation, annotationNode); JavacNode typeNode = annotationNode.up(); @@ -93,7 +96,9 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; - generateMethods(typeNode, annotationNode, members, callSuper, true, fieldAccess, onParam); + boolean cacheHashCode = ann.cacheStrategy() == CacheStrategy.LAZY; + + generateMethods(typeNode, annotationNode, members, callSuper, true, cacheHashCode, fieldAccess, onParam); } public void generateEqualsAndHashCodeForType(JavacNode typeNode, JavacNode source) { @@ -107,11 +112,11 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(typeNode, null, null); - generateMethods(typeNode, source, members, null, false, access, List.<JCAnnotation>nil()); + generateMethods(typeNode, source, members, null, false, false, access, List.<JCAnnotation>nil()); } public void generateMethods(JavacNode typeNode, JavacNode source, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, - Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<JCAnnotation> onParam) { + Boolean callSuper, boolean whineIfExists, boolean cacheHashCode, FieldAccess fieldAccess, List<JCAnnotation> onParam) { if (!isClass(typeNode)) { source.addError("@EqualsAndHashCode is only supported on a class."); @@ -129,30 +134,6 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas boolean isDirectDescendantOfObject = isDirectDescendantOfObject(typeNode); - if (isDirectDescendantOfObject && callSuper) { - source.addError("Generating equals/hashCode with a supercall to java.lang.Object is pointless."); - return; - } - - if (implicitCallSuper && !isDirectDescendantOfObject) { - CallSuperType cst = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_CALL_SUPER); - if (cst == null) cst = CallSuperType.WARN; - - switch (cst) { - default: - case WARN: - source.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type."); - callSuper = false; - break; - case SKIP: - callSuper = false; - break; - case CALL: - callSuper = true; - break; - } - } - boolean isFinal = (((JCClassDecl) typeNode.get()).mods.flags & Flags.FINAL) != 0; boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject; MemberExistsResult equalsExists = methodExists("equals", typeNode, 1); @@ -181,26 +162,72 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas //fallthrough } - JCMethodDecl equalsMethod = createEquals(typeNode, members, callSuper, fieldAccess, needsCanEqual, source.get(), onParam); + if (isDirectDescendantOfObject && callSuper) { + source.addError("Generating equals/hashCode with a supercall to java.lang.Object is pointless."); + return; + } + + if (implicitCallSuper && !isDirectDescendantOfObject) { + CallSuperType cst = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_CALL_SUPER); + if (cst == null) cst = CallSuperType.WARN; + + switch (cst) { + default: + case WARN: + source.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type."); + callSuper = false; + break; + case SKIP: + callSuper = false; + break; + case CALL: + callSuper = true; + break; + } + } + + JCMethodDecl equalsMethod = createEquals(typeNode, members, callSuper, fieldAccess, needsCanEqual, source, onParam); injectMethod(typeNode, equalsMethod); if (needsCanEqual && canEqualExists == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl canEqualMethod = createCanEqual(typeNode, source.get(), onParam); + JCMethodDecl canEqualMethod = createCanEqual(typeNode, source, onParam); injectMethod(typeNode, canEqualMethod); } - JCMethodDecl hashCodeMethod = createHashCode(typeNode, members, callSuper, fieldAccess, source.get()); + if (cacheHashCode){ + if (fieldExists(HASH_CODE_CACHE_NAME, typeNode) != MemberExistsResult.NOT_EXISTS) { + String msg = String.format("Not caching the result of hashCode: A field named %s already exists.", HASH_CODE_CACHE_NAME); + source.addWarning(msg); + cacheHashCode = false; + } else { + createHashCodeCacheField(typeNode, source); + } + } + + JCMethodDecl hashCodeMethod = createHashCode(typeNode, members, callSuper, cacheHashCode, fieldAccess, source); injectMethod(typeNode, hashCodeMethod); } + + private void createHashCodeCacheField(JavacNode typeNode, JavacNode source) { + JavacTreeMaker maker = typeNode.getTreeMaker(); + JCModifiers mods = maker.Modifiers(Flags.PRIVATE | Flags.TRANSIENT); + JCVariableDecl hashCodeCacheField = maker.VarDef(mods, typeNode.toName(HASH_CODE_CACHE_NAME), maker.TypeIdent(CTC_INT), null); + injectFieldAndMarkGenerated(typeNode, hashCodeCacheField); + recursiveSetGeneratedBy(hashCodeCacheField, source); + } - public JCMethodDecl createHashCode(JavacNode typeNode, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, boolean callSuper, FieldAccess fieldAccess, JCTree source) { + public JCMethodDecl createHashCode(JavacNode typeNode, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, boolean callSuper, boolean cacheHashCode, FieldAccess fieldAccess, JavacNode source) { JavacTreeMaker maker = typeNode.getTreeMaker(); JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil()); List<JCAnnotation> annsOnMethod = List.of(overrideAnnotation); CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode); - if (checkerFramework.generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())); + if (cacheHashCode && checkerFramework.generatePure()) { + annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__PURE), List.<JCExpression>nil())); + } else if (checkerFramework.generateSideEffectFree()) { + annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())); + } JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod); JCExpression returnType = maker.TypeIdent(CTC_INT); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); @@ -211,6 +238,15 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas boolean isEmpty = members.isEmpty(); + /* if (this.$hashCodeCache != 0) return this.$hashCodeCache; */ { + if (cacheHashCode) { + JCIdent receiver = maker.Ident(typeNode.toName("this")); + JCFieldAccess cacheHashCodeFieldAccess = maker.Select(receiver, typeNode.toName(HASH_CODE_CACHE_NAME)); + JCExpression cacheNotZero = maker.Binary(CTC_NOT_EQUAL, cacheHashCodeFieldAccess, maker.Literal(CTC_INT, 0)); + statements.append(maker.If(cacheNotZero, maker.Return(cacheHashCodeFieldAccess), null)); + } + } + /* final int PRIME = X; */ { if (!isEmpty) { statements.append(maker.VarDef(maker.Modifiers(finalFlag), primeName, maker.TypeIdent(CTC_INT), maker.Literal(HandlerUtil.primeForHashcode()))); @@ -228,7 +264,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas /* ... 1; */ init = maker.Literal(1); } - statements.append(maker.VarDef(maker.Modifiers(isEmpty ? finalFlag : 0), resultName, maker.TypeIdent(CTC_INT), init)); + statements.append(maker.VarDef(maker.Modifiers(isEmpty && !cacheHashCode ? finalFlag : 0L), resultName, maker.TypeIdent(CTC_INT), init)); } for (Included<JavacNode, EqualsAndHashCode.Include> member : members) { @@ -300,13 +336,27 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas } } + /* + * if (result == 0) result = Integer.MIN_VALUE; + * this.$hashCodeCache = result; + * + */ { + if (cacheHashCode) { + statements.append(maker.If(maker.Binary(CTC_EQUAL, maker.Ident(resultName), maker.Literal(CTC_INT, 0)), + maker.Exec(maker.Assign(maker.Ident(resultName), genJavaLangTypeRef(typeNode, "Integer", "MIN_VALUE"))), null)); + + JCFieldAccess cacheHashCodeFieldAccess = maker.Select(maker.Ident(typeNode.toName("this")), typeNode.toName(HASH_CODE_CACHE_NAME)); + statements.append(maker.Exec(maker.Assign(cacheHashCodeFieldAccess, maker.Ident(resultName)))); + } + } + /* return result; */ { statements.append(maker.Return(maker.Ident(resultName))); } JCBlock body = maker.Block(0, statements.toList()); return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("hashCode"), returnType, - List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext()); + List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source); } public JCExpressionStatement createResultCalculation(JavacNode typeNode, JCExpression expr) { @@ -368,7 +418,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas return maker.TypeApply(expr, wildcards.toList()); } - public JCMethodDecl createEquals(JavacNode typeNode, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual, JCTree source, List<JCAnnotation> onParam) { + public JCMethodDecl createEquals(JavacNode typeNode, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual, JavacNode source, List<JCAnnotation> onParam) { JavacTreeMaker maker = typeNode.getTreeMaker(); Name oName = typeNode.toName("o"); @@ -502,10 +552,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas } JCBlock body = maker.Block(0, statements.toList()); - return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("equals"), returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null), source, typeNode.getContext()); + return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("equals"), returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null), source); } - public JCMethodDecl createCanEqual(JavacNode typeNode, JCTree source, List<JCAnnotation> onParam) { + public JCMethodDecl createCanEqual(JavacNode typeNode, JavacNode source, List<JCAnnotation> onParam) { /* protected boolean canEqual(final java.lang.Object other) { * return other instanceof Outer.Inner.MyType; * } @@ -530,7 +580,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas JCBlock body = maker.Block(0, List.<JCStatement>of( maker.Return(maker.TypeTest(maker.Ident(otherName), createTypeReference(typeNode, false))))); - return recursiveSetGeneratedBy(maker.MethodDef(mods, canEqualName, returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null), source, typeNode.getContext()); + return recursiveSetGeneratedBy(maker.MethodDef(mods, canEqualName, returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null), source); } public JCStatement generateCompareFloatOrDouble(JCExpression thisDotField, JCExpression otherDotField, diff --git a/src/core/lombok/javac/handlers/HandleExtensionMethod.java b/src/core/lombok/javac/handlers/HandleExtensionMethod.java index 345c5f8e..dd565f72 100644 --- a/src/core/lombok/javac/handlers/HandleExtensionMethod.java +++ b/src/core/lombok/javac/handlers/HandleExtensionMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,6 +28,7 @@ import static lombok.javac.handlers.JavacResolver.*; import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.lang.model.element.ElementKind; @@ -37,12 +38,13 @@ import lombok.core.HandlerPriority; import lombok.experimental.ExtensionMethod; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; - -import org.mangosdk.spi.ProviderFor; +import lombok.javac.JavacResolution; +import lombok.spi.Provides; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.util.TreeScanner; import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Type; @@ -51,6 +53,7 @@ import com.sun.tools.javac.code.Type.ErrorType; import com.sun.tools.javac.code.Type.ForAll; import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -61,7 +64,7 @@ import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; /** * Handles the {@link ExtensionMethod} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(66560) // 2^16 + 2^10; we must run AFTER HandleVal which is at 2^16 public class HandleExtensionMethod extends JavacAnnotationHandler<ExtensionMethod> { @Override @@ -148,8 +151,9 @@ public class HandleExtensionMethod extends JavacAnnotationHandler<ExtensionMetho @Override public Void visitMethodInvocation(final MethodInvocationTree tree, final Void p) { + super.visitMethodInvocation(tree, p); handleMethodCall((JCMethodInvocation) tree); - return super.visitMethodInvocation(tree, p); + return null; } private void handleMethodCall(final JCMethodInvocation methodCall) { @@ -166,13 +170,25 @@ public class HandleExtensionMethod extends JavacAnnotationHandler<ExtensionMetho JCExpression receiver = receiverOf(methodCall); String methodName = methodNameOf(methodCall); - if ("this".equals(methodName) || "super".equals(methodName)) return; - Type resolvedMethodCall = CLASS_AND_METHOD.resolveMember(methodCallNode, methodCall); - if (resolvedMethodCall == null) return; - if (!suppressBaseMethods && !(resolvedMethodCall instanceof ErrorType)) return; - Type receiverType = CLASS_AND_METHOD.resolveMember(methodCallNode, receiver); - if (receiverType == null) return; - if (receiverType.tsym.toString().endsWith(receiver.toString())) return; + if ("this".equals(receiver.toString()) || "this".equals(methodName) || "super".equals(methodName)) return; + Map<JCTree, JCTree> resolution = new JavacResolution(methodCallNode.getContext()).resolveMethodMember(methodCallNode); + + JCTree resolvedMethodCall = resolution.get(methodCall); + if (resolvedMethodCall == null || resolvedMethodCall.type == null) return; + if (!suppressBaseMethods && !(resolvedMethodCall.type instanceof ErrorType)) return; + + JCTree resolvedReceiver = resolution.get(receiver); + if (resolvedReceiver == null || resolvedReceiver.type == null) return; + Type receiverType = resolvedReceiver.type; + + // Skip static method access + Symbol sym = null; + if (resolvedReceiver instanceof JCIdent) { + sym = ((JCIdent) resolvedReceiver).sym; + } else if (resolvedReceiver instanceof JCFieldAccess) { + sym = ((JCFieldAccess) resolvedReceiver).sym; + } + if (sym instanceof ClassSymbol) return; Types types = Types.instance(annotationNode.getContext()); for (Extension extension : extensions) { @@ -186,6 +202,7 @@ public class HandleExtensionMethod extends JavacAnnotationHandler<ExtensionMetho if (!types.isAssignable(receiverType, firstArgType)) continue; methodCall.args = methodCall.args.prepend(receiver); methodCall.meth = chainDotsString(annotationNode, extensionProvider.toString() + "." + methodName); + recursiveSetGeneratedBy(methodCall.meth, methodCallNode); return; } } diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java index c974bdb9..ebab67e3 100644 --- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java +++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2016 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,8 +34,7 @@ import lombok.experimental.PackagePrivate; import lombok.javac.JavacASTAdapter; import lombok.javac.JavacASTVisitor; import lombok.javac.JavacNode; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; @@ -46,7 +45,7 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl; /** * Handles the {@code lombok.FieldDefaults} annotation for javac. */ -@ProviderFor(JavacASTVisitor.class) +@Provides(JavacASTVisitor.class) @HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier. public class HandleFieldDefaults extends JavacASTAdapter { public boolean generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) { diff --git a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java index ec4015c7..f3c879d5 100644 --- a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2019 The Project Lombok Authors. + * Copyright (C) 2014-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,8 +36,7 @@ import lombok.experimental.FieldNameConstants; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; @@ -53,7 +52,7 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Name; -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameConstants> { private static final IdentifierName FIELDS = IdentifierName.valueOf("Fields"); @@ -78,7 +77,7 @@ public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameCo if (qualified.isEmpty()) { errorNode.addWarning("No fields qualify for @FieldNameConstants, therefore this annotation does nothing"); } else { - createInnerTypeFieldNameConstants(typeNode, errorNode, errorNode.get(), level, qualified, asEnum, innerTypeName, uppercase); + createInnerTypeFieldNameConstants(typeNode, errorNode, level, qualified, asEnum, innerTypeName, uppercase); } } @@ -133,7 +132,7 @@ public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameCo generateFieldNameConstantsForType(node, annotationNode, level, asEnum, innerTypeName, annotationInstance.onlyExplicitlyIncluded(), uppercase); } - private void createInnerTypeFieldNameConstants(JavacNode typeNode, JavacNode errorNode, JCTree pos, AccessLevel level, java.util.List<JavacNode> fields, boolean asEnum, IdentifierName innerTypeName, boolean uppercase) { + private void createInnerTypeFieldNameConstants(JavacNode typeNode, JavacNode errorNode, AccessLevel level, java.util.List<JavacNode> fields, boolean asEnum, IdentifierName innerTypeName, boolean uppercase) { if (fields.isEmpty()) return; JavacTreeMaker maker = typeNode.getTreeMaker(); @@ -146,7 +145,7 @@ public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameCo if (fieldsType == null) { JCClassDecl innerType = maker.ClassDef(mods, fieldsName, List.<JCTypeParameter>nil(), null, List.<JCExpression>nil(), List.<JCTree>nil()); fieldsType = injectType(typeNode, innerType); - recursiveSetGeneratedBy(innerType, pos, typeNode.getContext()); + recursiveSetGeneratedBy(innerType, errorNode); genConstr = true; } else { JCClassDecl builderTypeDeclaration = (JCClassDecl) fieldsType.get(); @@ -166,7 +165,7 @@ public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameCo JCModifiers genConstrMods = maker.Modifiers(Flags.GENERATEDCONSTR | (asEnum ? 0L : Flags.PRIVATE)); JCBlock genConstrBody = maker.Block(0L, List.<JCStatement>of(maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("super")), List.<JCExpression>nil())))); JCMethodDecl c = maker.MethodDef(genConstrMods, typeNode.toName("<init>"), null, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), genConstrBody, null); - recursiveSetGeneratedBy(c, pos, typeNode.getContext()); + recursiveSetGeneratedBy(c, errorNode); injectMethod(fieldsType, c); } @@ -187,9 +186,9 @@ public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameCo } JCVariableDecl constantField = maker.VarDef(constantValueMods, fName, returnType, init); injectField(fieldsType, constantField, false, true); - setGeneratedBy(constantField, pos, typeNode.getContext()); + setGeneratedBy(constantField, errorNode); generated.add(constantField); } - for (JCVariableDecl cf : generated) recursiveSetGeneratedBy(cf, pos, typeNode.getContext()); + for (JCVariableDecl cf : generated) recursiveSetGeneratedBy(cf, errorNode); } } diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 1b4d404b..8f6de9bb 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -43,8 +43,7 @@ import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.JavacTreeMaker.TypeTag; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Type; @@ -70,8 +69,10 @@ import com.sun.tools.javac.util.Name; /** * Handles the {@code lombok.Getter} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleGetter extends JavacAnnotationHandler<Getter> { + private static final String GETTER_NODE_NOT_SUPPORTED_ERR = "@Getter is only supported on a class, an enum, or a field."; + public void generateGetterForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelGetter, List<JCAnnotation> onMethod) { if (checkForTypeLevelGetter) { if (hasAnnotation(Getter.class, typeNode)) { @@ -81,7 +82,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { } if (!isClassOrEnum(typeNode)) { - errorNode.addError("@Getter is only supported on a class, an enum, or a field."); + errorNode.addError(GETTER_NODE_NOT_SUPPORTED_ERR); return; } @@ -165,7 +166,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { JavacNode fieldNode, JavacNode source, boolean whineIfExists, boolean lazy, List<JCAnnotation> onMethod) { if (fieldNode.getKind() != Kind.FIELD) { - source.addError("@Getter is only supported on a class or a field."); + source.addError(GETTER_NODE_NOT_SUPPORTED_ERR); return; } @@ -213,14 +214,14 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC); - injectMethod(fieldNode.up(), createGetter(access, fieldNode, fieldNode.getTreeMaker(), source.get(), lazy, onMethod), List.<Type>nil(), getMirrorForFieldType(fieldNode)); + injectMethod(fieldNode.up(), createGetter(access, fieldNode, fieldNode.getTreeMaker(), source, lazy, onMethod), List.<Type>nil(), getMirrorForFieldType(fieldNode)); } - public JCMethodDecl createGetter(long access, JavacNode field, JavacTreeMaker treeMaker, JCTree source, boolean lazy, List<JCAnnotation> onMethod) { + public JCMethodDecl createGetter(long access, JavacNode field, JavacTreeMaker treeMaker, JavacNode source, boolean lazy, List<JCAnnotation> onMethod) { JCVariableDecl fieldNode = (JCVariableDecl) field.get(); // Remember the type; lazy will change it - JCExpression methodType = cloneType(treeMaker, copyType(treeMaker, fieldNode), source, field.getContext()); + JCExpression methodType = cloneType(treeMaker, copyType(treeMaker, fieldNode), source); // Generate the methodName; lazy will change the field type Name methodName = field.toName(toGetterName(field)); @@ -261,9 +262,9 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { if (isFieldDeprecated(field)) annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.<JCExpression>nil())); JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType, - methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source, field.getContext()); + methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source); - if (toClearOfMarkers != null) recursiveSetGeneratedBy(toClearOfMarkers, null, null); + if (toClearOfMarkers != null) recursiveSetGeneratedBy(toClearOfMarkers, null); if (methodArgPos != null) { for (int i = 0; i < methodArgPos.length; i++) { ((JCMethodInvocation) toClearOfMarkers).args.get(i).pos = methodArgPos[i]; @@ -274,10 +275,10 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { if (addSuppressWarningsUnchecked) { ListBuffer<JCExpression> suppressions = new ListBuffer<JCExpression>(); if (!Boolean.FALSE.equals(field.getAst().readConfiguration(ConfigurationKeys.ADD_SUPPRESSWARNINGS_ANNOTATIONS))) { - suppressions.add(treeMaker.Literal("all")); + suppressions.append(treeMaker.Literal("all")); } - suppressions.add(treeMaker.Literal("unchecked")); - addAnnotation(decl.mods, field, source.pos, source, field.getContext(), "java.lang.SuppressWarnings", treeMaker.NewArray(null, List.<JCExpression>nil(), suppressions.toList())); + suppressions.append(treeMaker.Literal("unchecked")); + addAnnotation(decl.mods, field, source, "java.lang.SuppressWarnings", treeMaker.NewArray(null, List.<JCExpression>nil(), suppressions.toList())); } copyJavadoc(field, decl, CopyJavadoc.GETTER); @@ -328,7 +329,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { TYPE_MAP = Collections.unmodifiableMap(m); } - public List<JCStatement> createLazyGetterBody(JavacTreeMaker maker, JavacNode fieldNode, JCTree source) { + public List<JCStatement> createLazyGetterBody(JavacTreeMaker maker, JavacNode fieldNode, JavacNode source) { /* java.lang.Object value = this.fieldName.get(); if (value == null) { @@ -374,7 +375,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { /* java.lang.Object value = this.fieldName.get();*/ { JCExpression valueVarType = genJavaLangTypeRef(fieldNode, "Object"); - statements.append(maker.VarDef(maker.Modifiers(0), valueName, valueVarType, callGet(fieldNode, createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD)))); + statements.append(maker.VarDef(maker.Modifiers(0L), valueName, valueVarType, callGet(fieldNode, createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD)))); } /* if (value == null) { */ { @@ -441,8 +442,8 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { /* private final java.util.concurrent.atomic.AtomicReference<Object> fieldName = new java.util.concurrent.atomic.AtomicReference<Object>(); */ { field.vartype = recursiveSetGeneratedBy( - maker.TypeApply(chainDotsString(fieldNode, AR), List.<JCExpression>of(genJavaLangTypeRef(fieldNode, "Object"))), source, fieldNode.getContext()); - field.init = recursiveSetGeneratedBy(maker.NewClass(null, NIL_EXPRESSION, copyType(maker, field), NIL_EXPRESSION, null), source, fieldNode.getContext()); + maker.TypeApply(chainDotsString(fieldNode, AR), List.<JCExpression>of(genJavaLangTypeRef(fieldNode, "Object"))), source); + field.init = recursiveSetGeneratedBy(maker.NewClass(null, NIL_EXPRESSION, copyType(maker, field), NIL_EXPRESSION, null), source); } return statements.toList(); diff --git a/src/core/lombok/javac/handlers/HandleHelper.java b/src/core/lombok/javac/handlers/HandleHelper.java index 6f4361c1..35b0c4f2 100755 --- a/src/core/lombok/javac/handlers/HandleHelper.java +++ b/src/core/lombok/javac/handlers/HandleHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2016 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,8 +29,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.Iterator; -import org.mangosdk.spi.ProviderFor; - import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.TreeVisitor; import com.sun.source.util.TreeScanner; @@ -56,8 +54,9 @@ import lombok.experimental.Helper; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; +import lombok.spi.Provides; -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleHelper extends JavacAnnotationHandler<Helper> { private List<JCStatement> getStatementsFromJcNode(JCTree tree) { if (tree instanceof JCBlock) return ((JCBlock) tree).stats; @@ -71,7 +70,7 @@ public class HandleHelper extends JavacAnnotationHandler<Helper> { else throw new IllegalArgumentException("Can't set statements on node type: " + tree.getClass()); } - @Override public void handle(AnnotationValues<Helper> annotation, JCAnnotation ast, JavacNode annotationNode) { + @Override public void handle(AnnotationValues<Helper> annotation, JCAnnotation ast, final JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.HELPER_FLAG_USAGE, "@Helper"); deleteAnnotationIfNeccessary(annotationNode, Helper.class); @@ -120,6 +119,7 @@ public class HandleHelper extends JavacAnnotationHandler<Helper> { JCIdent jci = (JCIdent) jcmi.meth; if (Arrays.binarySearch(knownMethodNames_, jci.name.toString()) < 0) return; jcmi.meth = maker.Select(maker.Ident(helperName), jci.name); + recursiveSetGeneratedBy(jcmi.meth, annotationNode); helperUsed[0] = true; } }; @@ -144,6 +144,7 @@ public class HandleHelper extends JavacAnnotationHandler<Helper> { JCExpression init = maker.NewClass(null, List.<JCExpression>nil(), maker.Ident(annotatedType_.name), List.<JCExpression>nil(), null); JCExpression varType = maker.Ident(annotatedType_.name); JCVariableDecl decl = maker.VarDef(maker.Modifiers(Flags.FINAL), helperName, varType, init); + recursiveSetGeneratedBy(decl, annotationNode); newStatements.append(decl); } setStatementsOfJcNode(containingBlock.get(), newStatements.toList()); diff --git a/src/core/lombok/javac/handlers/HandleJacksonized.java b/src/core/lombok/javac/handlers/HandleJacksonized.java index 0aa02d1b..3ee0eec4 100644 --- a/src/core/lombok/javac/handlers/HandleJacksonized.java +++ b/src/core/lombok/javac/handlers/HandleJacksonized.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Project Lombok Authors. + * Copyright (C) 2020-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,8 +24,6 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; import static lombok.javac.handlers.JavacHandlerUtil.*; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; @@ -49,13 +47,14 @@ import lombok.extern.jackson.Jacksonized; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; +import lombok.spi.Provides; /** * This (javac) handler deals with {@code @Jacksonized} modifying the (already * generated) {@code @Builder} or {@code @SuperBuilder} to conform to Jackson's * needs for builders. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(-512) // Above Handle(Super)Builder's level (builders must be already generated). public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> { @@ -107,7 +106,7 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> { JavacTreeMaker maker = annotatedNode.getTreeMaker(); // Now lets find the generated builder class. - String builderClassName = getBuilderClassName(ast, annotationNode, annotatedNode, td, builderAnnotation, maker); + String builderClassName = getBuilderClassName(annotationNode, annotatedNode, td, builderAnnotation, maker); JCClassDecl builderClass = null; for (JCTree member : td.getMembers()) { @@ -132,11 +131,15 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> { JCFieldAccess builderClassReference = maker.Select(builderClassExpression, annotatedNode.toName("class")); JCExpression assign = maker.Assign(maker.Ident(annotationNode.toName("builder")), builderClassReference); JCAnnotation annotationJsonDeserialize = maker.Annotation(jsonDeserializeType, List.of(assign)); + recursiveSetGeneratedBy(annotationJsonDeserialize, annotationNode); td.mods.annotations = td.mods.annotations.append(annotationJsonDeserialize); // Copy annotations from the class to the builder class. List<JCAnnotation> copyableAnnotations = findJacksonAnnotationsOnClass(tdNode); List<JCAnnotation> copiedAnnotations = copyAnnotations(copyableAnnotations); + for (JCAnnotation anno : copiedAnnotations) { + recursiveSetGeneratedBy(anno, annotationNode); + } builderClass.mods.annotations = builderClass.mods.annotations.appendList(copiedAnnotations); // Insert @JsonPOJOBuilder on the builder class. @@ -144,6 +147,7 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> { JCExpression withPrefixExpr = maker.Assign(maker.Ident(annotationNode.toName("withPrefix")), maker.Literal(setPrefix)); JCExpression buildMethodNameExpr = maker.Assign(maker.Ident(annotationNode.toName("buildMethodName")), maker.Literal(buildMethodName)); JCAnnotation annotationJsonPOJOBuilder = maker.Annotation(jsonPOJOBuilderType, List.of(withPrefixExpr, buildMethodNameExpr)); + recursiveSetGeneratedBy(annotationJsonPOJOBuilder, annotatedNode); builderClass.mods.annotations = builderClass.mods.annotations.append(annotationJsonPOJOBuilder); // @SuperBuilder? Make it package-private! @@ -151,7 +155,7 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> { builderClass.mods.flags = builderClass.mods.flags & ~Flags.PRIVATE; } - private String getBuilderClassName(JCAnnotation ast, JavacNode annotationNode, JavacNode annotatedNode, JCClassDecl td, AnnotationValues<Builder> builderAnnotation, JavacTreeMaker maker) { + private String getBuilderClassName(JavacNode annotationNode, JavacNode annotatedNode, JCClassDecl td, AnnotationValues<Builder> builderAnnotation, JavacTreeMaker maker) { String builderClassName = builderAnnotation != null ? builderAnnotation.getInstance().builderClassName() : null; if (builderClassName == null || builderClassName.isEmpty()) { @@ -166,7 +170,7 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> { JCExpression returnType = fillParametersFrom.restype; List<JCTypeParameter> typeParams = fillParametersFrom.typarams; if (returnType instanceof JCTypeApply) { - returnType = cloneType(maker, returnType, ast, annotationNode.getContext()); + returnType = cloneType(maker, returnType, annotatedNode); } replacement = HandleBuilder.returnTypeToBuilderClassName(annotationNode, td, returnType, typeParams); } else { diff --git a/src/core/lombok/javac/handlers/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java index 1ff10ad1..47c4098f 100644 --- a/src/core/lombok/javac/handlers/HandleLog.java +++ b/src/core/lombok/javac/handlers/HandleLog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2019 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,15 +31,14 @@ import lombok.core.configuration.IdentifierName; import lombok.core.configuration.LogDeclaration; import lombok.core.configuration.LogDeclaration.LogFactoryParameter; import lombok.core.handlers.LoggingFramework; +import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -75,6 +74,7 @@ public class HandleLog { annotationNode.addWarning("Field '" + logFieldName + "' already exists."); return; } + if (isRecord(typeNode) && !useStatic) { annotationNode.addError("Logger fields must be static in records."); return; @@ -82,7 +82,7 @@ public class HandleLog { Object valueGuess = annotation.getValueGuess("topic"); JCExpression loggerTopic = (JCExpression) annotation.getActualExpression("topic"); - + if (valueGuess instanceof String && ((String) valueGuess).trim().isEmpty()) loggerTopic = null; if (framework.getDeclaration().getParametersWithTopic() == null && loggerTopic != null) { annotationNode.addError(framework.getAnnotationAsString() + " does not allow a topic."); @@ -94,7 +94,7 @@ public class HandleLog { } JCFieldAccess loggingType = selfType(typeNode); - createField(framework, typeNode, loggingType, annotationNode.get(), logFieldName.getName(), useStatic, loggerTopic); + createField(framework, typeNode, loggingType, annotationNode, logFieldName.getName(), useStatic, loggerTopic); break; default: annotationNode.addError("@Log is legal only on types."); @@ -108,7 +108,7 @@ public class HandleLog { return maker.Select(maker.Ident(name), typeNode.toName("class")); } - private static boolean createField(LoggingFramework framework, JavacNode typeNode, JCFieldAccess loggingType, JCTree source, String logFieldName, boolean useStatic, JCExpression loggerTopic) { + private static boolean createField(LoggingFramework framework, JavacNode typeNode, JCFieldAccess loggingType, JavacNode source, String logFieldName, boolean useStatic, JCExpression loggerTopic) { JavacTreeMaker maker = typeNode.getTreeMaker(); LogDeclaration logDeclaration = framework.getDeclaration(); @@ -122,17 +122,19 @@ public class HandleLog { JCVariableDecl fieldDecl = recursiveSetGeneratedBy(maker.VarDef( maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (useStatic ? Flags.STATIC : 0)), - typeNode.toName(logFieldName), loggerType, factoryMethodCall), source, typeNode.getContext()); + typeNode.toName(logFieldName), loggerType, factoryMethodCall), source); - // This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8243057 - if (isRecord(typeNode)) { + if (isRecord(typeNode) && Javac.getJavaCompilerVersion() < 16) { + // This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8243057 + injectField(typeNode, fieldDecl); } else { injectFieldAndMarkGenerated(typeNode, fieldDecl); } + return true; } - + private static JCExpression[] createFactoryParameters(JavacNode typeNode, JCFieldAccess loggingType, java.util.List<LogFactoryParameter> parameters, JCExpression loggerTopic) { JCExpression[] expressions = new JCExpression[parameters.size()]; JavacTreeMaker maker = typeNode.getTreeMaker(); @@ -164,7 +166,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.apachecommons.CommonsLog} annotation for javac. */ - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleCommonsLog extends JavacAnnotationHandler<lombok.extern.apachecommons.CommonsLog> { @Override public void handle(AnnotationValues<lombok.extern.apachecommons.CommonsLog> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_COMMONS_FLAG_USAGE, "@apachecommons.CommonsLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -175,7 +177,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.java.Log} annotation for javac. */ - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleJulLog extends JavacAnnotationHandler<lombok.extern.java.Log> { @Override public void handle(AnnotationValues<lombok.extern.java.Log> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_JUL_FLAG_USAGE, "@java.Log", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -186,7 +188,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.log4j.Log4j} annotation for javac. */ - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleLog4jLog extends JavacAnnotationHandler<lombok.extern.log4j.Log4j> { @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_LOG4J_FLAG_USAGE, "@Log4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -197,7 +199,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.log4j.Log4j2} annotation for javac. */ - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleLog4j2Log extends JavacAnnotationHandler<lombok.extern.log4j.Log4j2> { @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j2> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_LOG4J2_FLAG_USAGE, "@Log4j2", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -208,7 +210,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.slf4j.Slf4j} annotation for javac. */ - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleSlf4jLog extends JavacAnnotationHandler<lombok.extern.slf4j.Slf4j> { @Override public void handle(AnnotationValues<lombok.extern.slf4j.Slf4j> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_SLF4J_FLAG_USAGE, "@Slf4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -219,7 +221,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.slf4j.XSlf4j} annotation for javac. */ - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleXSlf4jLog extends JavacAnnotationHandler<lombok.extern.slf4j.XSlf4j> { @Override public void handle(AnnotationValues<lombok.extern.slf4j.XSlf4j> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_XSLF4J_FLAG_USAGE, "@XSlf4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -230,7 +232,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.jbosslog.JBossLog} annotation for javac. */ - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleJBossLog extends JavacAnnotationHandler<lombok.extern.jbosslog.JBossLog> { @Override public void handle(AnnotationValues<lombok.extern.jbosslog.JBossLog> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_JBOSSLOG_FLAG_USAGE, "@JBossLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -241,7 +243,7 @@ public class HandleLog { /** * Handles the {@link lombok.extern.flogger.Flogger} annotation for javac. */ - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleFloggerLog extends JavacAnnotationHandler<lombok.extern.flogger.Flogger> { @Override public void handle(AnnotationValues<lombok.extern.flogger.Flogger> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_FLOGGER_FLAG_USAGE, "@Flogger", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); @@ -252,7 +254,7 @@ public class HandleLog { /** * Handles the {@link lombok.CustomLog} annotation for javac. */ - @ProviderFor(JavacAnnotationHandler.class) + @Provides public static class HandleCustomLog extends JavacAnnotationHandler<lombok.CustomLog> { @Override public void handle(AnnotationValues<lombok.CustomLog> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_CUSTOM_FLAG_USAGE, "@CustomLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); diff --git a/src/core/lombok/javac/handlers/HandleNonNull.java b/src/core/lombok/javac/handlers/HandleNonNull.java index 6b6b7efa..2f359c0f 100644 --- a/src/core/lombok/javac/handlers/HandleNonNull.java +++ b/src/core/lombok/javac/handlers/HandleNonNull.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2019 The Project Lombok Authors. + * Copyright (C) 2013-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,8 +27,6 @@ import static lombok.javac.JavacTreeMaker.TreeTag.treeTag; import static lombok.javac.JavacTreeMaker.TypeTag.typeTag; import static lombok.javac.handlers.JavacHandlerUtil.*; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCAssert; import com.sun.tools.javac.tree.JCTree.JCAssign; @@ -57,17 +55,16 @@ import lombok.NonNull; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; - -import lombok.javac.Java14Flags; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists; +import lombok.spi.Provides; -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(value = 512) // 2^9; onParameter=@__(@NonNull) has to run first. public class HandleNonNull extends JavacAnnotationHandler<NonNull> { private HandleConstructor handleConstructor = new HandleConstructor(); - + @Override public void handle(AnnotationValues<NonNull> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.NON_NULL_FLAG_USAGE, "@NonNull"); @@ -117,7 +114,7 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> { // and if they exist, create a new method in the class: 'private static <T> T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and // wrap all references to it in the super/this to a call to this method. - JCStatement nullCheck = recursiveSetGeneratedBy(generateNullCheck(annotationNode.getTreeMaker(), paramNode, annotationNode), ast, annotationNode.getContext()); + JCStatement nullCheck = recursiveSetGeneratedBy(generateNullCheck(annotationNode.getTreeMaker(), paramNode, annotationNode), annotationNode); if (nullCheck == null) { // @NonNull applied to a primitive. Kinda pointless. Let's generate a warning. @@ -125,19 +122,18 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> { return; } + List<JCStatement> statements = declaration.body.stats; + + String expectedName = paramNode.getName(); + JavacNode typeNode = upToTypeNode(annotationNode); - if ((declaration.mods.flags & Java14Flags.RECORD) != 0) { - if (!lombokConstructorExists(typeNode)) { + if ((declaration.mods.flags & RECORD) != 0) { + if (!lombokConstructorExists(typeNode)) { handleConstructor.generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, null, SkipIfConstructorExists.NO, annotationNode); } - return; } - List<JCStatement> statements = declaration.body.stats; - - String expectedName = paramNode.getName(); - /* Abort if the null check is already there, delving into try and synchronized statements */ { List<JCStatement> stats = statements; int idx = 0; diff --git a/src/core/lombok/javac/handlers/HandlePrintAST.java b/src/core/lombok/javac/handlers/HandlePrintAST.java index 0826d1d1..647139bc 100644 --- a/src/core/lombok/javac/handlers/HandlePrintAST.java +++ b/src/core/lombok/javac/handlers/HandlePrintAST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,8 +25,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.PrintStream; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.tree.JCTree.JCAnnotation; import lombok.Lombok; @@ -36,11 +34,12 @@ import lombok.core.PrintAST; import lombok.javac.JavacASTVisitor; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; +import lombok.spi.Provides; /** * Handles the {@code lombok.core.PrintAST} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(536870912) // 2^29; this handler is customarily run at the very end. public class HandlePrintAST extends JavacAnnotationHandler<PrintAST> { @Override public void handle(AnnotationValues<PrintAST> annotation, JCAnnotation ast, JavacNode annotationNode) { diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index b51574b7..5c34f9f5 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,8 +37,7 @@ import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol.ClassSymbol; @@ -60,8 +59,10 @@ import com.sun.tools.javac.util.Name; /** * Handles the {@code lombok.Setter} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleSetter extends JavacAnnotationHandler<Setter> { + private static final String SETTER_NODE_NOT_SUPPORTED_ERR = "@Setter is only supported on a class or a field."; + public void generateSetterForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelSetter, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { if (checkForTypeLevelSetter) { if (hasAnnotation(Setter.class, typeNode)) { @@ -71,7 +72,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { } if (!isClass(typeNode)) { - errorNode.addError("@Setter is only supported on a class or a field."); + errorNode.addError(SETTER_NODE_NOT_SUPPORTED_ERR); return; } @@ -145,7 +146,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { public void createSetterForField(AccessLevel level, JavacNode fieldNode, JavacNode sourceNode, boolean whineIfExists, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { if (fieldNode.getKind() != Kind.FIELD) { - fieldNode.addError("@Setter is only supported on a class or a field."); + fieldNode.addError(SETTER_NODE_NOT_SUPPORTED_ERR); return; } @@ -215,13 +216,36 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { List<JCAnnotation> annotations = d.mods.annotations; if (annotations == null) annotations = List.nil(); JCAnnotation anno = treeMaker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.<JCExpression>nil()); - recursiveSetGeneratedBy(anno, source.get(), field.getContext()); + recursiveSetGeneratedBy(anno, source); + d.mods.annotations = annotations.prepend(anno); + } + return d; + } + + public static JCMethodDecl createSetterWithRecv(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name paramName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam, JCVariableDecl recv) { + JCExpression returnType = null; + JCReturn returnStatement = null; + if (shouldReturnThis) { + returnType = cloneSelfType(field); + returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this"))); + } + + JCMethodDecl d = createSetterWithRecv(access, deprecate, field, treeMaker, setterName, paramName, booleanFieldToSet, returnType, returnStatement, source, onMethod, onParam, recv); + if (shouldReturnThis && getCheckerFrameworkVersion(source).generateReturnsReceiver()) { + List<JCAnnotation> annotations = d.mods.annotations; + if (annotations == null) annotations = List.nil(); + JCAnnotation anno = treeMaker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.<JCExpression>nil()); + recursiveSetGeneratedBy(anno, source); d.mods.annotations = annotations.prepend(anno); } return d; } public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name paramName, Name booleanFieldToSet, JCExpression methodType, JCStatement returnStatement, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { + return createSetterWithRecv(access, deprecate, field, treeMaker, setterName, paramName, booleanFieldToSet, methodType, returnStatement, source, onMethod, onParam, null); + } + + public static JCMethodDecl createSetterWithRecv(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name paramName, Name booleanFieldToSet, JCExpression methodType, JCStatement returnStatement, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam, JCVariableDecl recv) { if (setterName == null) return null; JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); @@ -237,7 +261,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { List<JCAnnotation> annsOnParam = copyAnnotations(onParam).appendList(copyableAnnotations); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, field.getContext()); - JCExpression pType = cloneType(treeMaker, fieldDecl.vartype, source.get(), source.getContext()); + JCExpression pType = cloneType(treeMaker, fieldDecl.vartype, source); JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(flags, annsOnParam), paramName, pType, null); if (!hasNonNullAnnotations(field) && !hasNonNullAnnotations(field, onParam)) { @@ -272,10 +296,16 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.<JCExpression>nil())); } - JCMethodDecl methodDef = treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType, - methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue); + JCMethodDecl methodDef; + if (recv != null && treeMaker.hasMethodDefWithRecvParam()) { + methodDef = treeMaker.MethodDefWithRecvParam(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType, + methodGenericParams, recv, parameters, throwsClauses, methodBody, annotationMethodDefaultValue); + } else { + methodDef = treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType, + methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue); + } if (returnStatement != null) createRelevantNonNullAnnotation(source, methodDef); - JCMethodDecl decl = recursiveSetGeneratedBy(methodDef, source.get(), field.getContext()); + JCMethodDecl decl = recursiveSetGeneratedBy(methodDef, source); copyJavadoc(field, decl, CopyJavadoc.SETTER, returnStatement != null); return decl; } diff --git a/src/core/lombok/javac/handlers/HandleSneakyThrows.java b/src/core/lombok/javac/handlers/HandleSneakyThrows.java index ffe37a4c..3dde75d8 100644 --- a/src/core/lombok/javac/handlers/HandleSneakyThrows.java +++ b/src/core/lombok/javac/handlers/HandleSneakyThrows.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,11 +35,9 @@ import lombok.core.HandlerPriority; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; @@ -48,14 +46,13 @@ import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTry; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; -import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import lombok.javac.Javac; /** * Handles the {@code lombok.SneakyThrows} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleSneakyThrows extends JavacAnnotationHandler<SneakyThrows> { @Override public void handle(AnnotationValues<SneakyThrows> annotation, JCAnnotation ast, JavacNode annotationNode) { @@ -107,7 +104,7 @@ public class HandleSneakyThrows extends JavacAnnotationHandler<SneakyThrows> { } for (String exception : exceptions) { - contents = List.of(buildTryCatchBlock(methodNode, contents, exception, annotation.get())); + contents = List.of(buildTryCatchBlock(methodNode, contents, exception, annotation)); } method.body.stats = isConstructorCall ? List.of(constructorCall).appendList(contents) : contents; @@ -122,11 +119,10 @@ public class HandleSneakyThrows extends JavacAnnotationHandler<SneakyThrows> { } } - public JCStatement buildTryCatchBlock(JavacNode node, List<JCStatement> contents, String exception, JCTree source) { + public JCStatement buildTryCatchBlock(JavacNode node, List<JCStatement> contents, String exception, JavacNode source) { JavacTreeMaker maker = node.getTreeMaker(); - Context context = node.getContext(); - JCBlock tryBlock = setGeneratedBy(maker.Block(0, contents), source, context); + JCBlock tryBlock = setGeneratedBy(maker.Block(0, contents), source); JCExpression varType = chainDots(node, exception.split("\\.")); JCVariableDecl catchParam = maker.VarDef(maker.Modifiers(Flags.FINAL | Flags.PARAMETER), node.toName("$ex"), varType, null); @@ -134,7 +130,7 @@ public class HandleSneakyThrows extends JavacAnnotationHandler<SneakyThrows> { JCBlock catchBody = maker.Block(0, List.<JCStatement>of(maker.Throw(maker.Apply( List.<JCExpression>nil(), lombokLombokSneakyThrowNameRef, List.<JCExpression>of(maker.Ident(node.toName("$ex"))))))); - JCTry tryStatement = maker.Try(tryBlock, List.of(recursiveSetGeneratedBy(maker.Catch(catchParam, catchBody), source, context)), null); + JCTry tryStatement = maker.Try(tryBlock, List.of(recursiveSetGeneratedBy(maker.Catch(catchParam, catchBody), source)), null); if (JavacHandlerUtil.inNetbeansEditor(node)) { //set span (start and end position) of the try statement and the main block //this allows NetBeans to dive into the statement correctly: @@ -146,6 +142,6 @@ public class HandleSneakyThrows extends JavacAnnotationHandler<SneakyThrows> { Javac.storeEnd(tryBlock, endPos, top); Javac.storeEnd(tryStatement, endPos, top); } - return setGeneratedBy(tryStatement, source, context); + return setGeneratedBy(tryStatement, source); } } diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index be6ddae2..9185cedf 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2020 The Project Lombok Authors. + * Copyright (C) 2013-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,6 +21,7 @@ */ package lombok.javac.handlers; +import static lombok.javac.handlers.HandleBuilder.*; import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; @@ -31,8 +32,6 @@ import java.util.HashSet; import javax.lang.model.element.Modifier; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; @@ -53,7 +52,6 @@ import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWildcard; -import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; @@ -78,68 +76,102 @@ import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.HandleBuilder.BuilderFieldData; +import lombok.javac.handlers.HandleBuilder.BuilderJob; import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult; import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; +import lombok.spi.Provides; -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes. public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { private static final String SELF_METHOD = "self"; - private static final String TO_BUILDER_METHOD_NAME = "toBuilder"; private static final String FILL_VALUES_METHOD_NAME = "$fillValuesFrom"; private static final String STATIC_FILL_VALUES_METHOD_NAME = "$fillValuesFromInstanceIntoBuilder"; private static final String INSTANCE_VARIABLE_NAME = "instance"; private static final String BUILDER_VARIABLE_NAME = "b"; - + + class SuperBuilderJob extends BuilderJob { + JavacNode builderAbstractType; + String builderAbstractClassName; + JavacNode builderImplType; + String builderImplClassName; + List<JCTypeParameter> builderTypeParams_; + + void init(AnnotationValues<SuperBuilder> annValues, SuperBuilder ann, JavacNode node) { + accessOuters = accessInners = AccessLevel.PUBLIC; + oldFluent = true; + oldChain = true; + + builderMethodName = ann.builderMethodName(); + buildMethodName = ann.buildMethodName(); + toBuilder = ann.toBuilder(); + + if (builderMethodName == null) builderMethodName = "builder"; + if (buildMethodName == null) buildMethodName = "build"; + builderClassName = fixBuilderClassName(node, ""); + } + + void setBuilderToImpl() { + builderType = builderImplType; + builderClassName = builderImplClassName; + builderTypeParams = typeParams; + } + + void setBuilderToAbstract() { + builderType = builderAbstractType; + builderClassName = builderAbstractClassName; + builderTypeParams = builderTypeParams_; + } + } + @Override public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast, JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); - CheckerFrameworkVersion cfv = getCheckerFrameworkVersion(annotationNode); - SuperBuilder superbuilderAnnotation = annotation.getInstance(); - // Do not delete the SuperBuilder annotation here, we need it for @Jacksonized. + SuperBuilderJob job = new SuperBuilderJob(); + job.sourceNode = annotationNode; + job.checkerFramework = getCheckerFrameworkVersion(annotationNode); + job.isStatic = true; - String builderMethodName = superbuilderAnnotation.builderMethodName(); - String buildMethodName = superbuilderAnnotation.buildMethodName(); + SuperBuilder annInstance = annotation.getInstance(); - if (builderMethodName == null) builderMethodName = "builder"; - if (buildMethodName == null) buildMethodName = "build"; + job.init(annotation, annInstance, annotationNode); boolean generateBuilderMethod; - if (builderMethodName.isEmpty()) { + if (job.builderMethodName.isEmpty()) { generateBuilderMethod = false; - } else if (!checkName("builderMethodName", builderMethodName, annotationNode)) { + } else if (!checkName("builderMethodName", job.builderMethodName, annotationNode)) { return; } else { generateBuilderMethod = true; } - if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; + if (!checkName("buildMethodName", job.buildMethodName, annotationNode)) return; - boolean toBuilder = superbuilderAnnotation.toBuilder(); + // Do not delete the SuperBuilder annotation here, we need it for @Jacksonized. - JavacNode tdParent = annotationNode.up(); + JavacNode parent = annotationNode.up(); - java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>(); - List<JCTypeParameter> typeParams = List.nil(); - List<JCExpression> thrownExceptions = List.nil(); + job.builderFields = new ArrayList<BuilderFieldData>(); + job.typeParams = List.nil(); + List<JCExpression> buildMethodThrownExceptions = List.nil(); List<JCExpression> superclassTypeParams = List.nil(); - boolean addCleaning = false; - if (!isClass(tdParent)) { - annotationNode.addError("@SuperBuilder is only supported on types."); + if (!isClass(parent)) { + annotationNode.addError("@SuperBuilder is only supported on classes."); return; } + job.parentType = parent; + JCClassDecl td = (JCClassDecl) parent.get(); + // Gather all fields of the class that should be set by the builder. - JCClassDecl td = (JCClassDecl) tdParent.get(); - ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>(); ArrayList<JavacNode> nonFinalNonDefaultedFields = null; - boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent)); - for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { + boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); + for (JavacNode fieldNode : HandleConstructor.findAllFields(parent, true)) { JCVariableDecl fd = (JCVariableDecl) fieldNode.get(); JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, true); boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); @@ -149,7 +181,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { bfd.builderFieldName = bfd.name; bfd.annotations = findCopyableAnnotations(fieldNode); bfd.type = fd.vartype; - bfd.singularData = getSingularData(fieldNode, superbuilderAnnotation.setterPrefix()); + bfd.singularData = getSingularData(fieldNode, annInstance.setterPrefix()); bfd.originalFieldNode = fieldNode; if (bfd.singularData != null && isDefault != null) { @@ -169,44 +201,20 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { } if (isDefault != null) { - bfd.nameOfDefaultProvider = tdParent.toName("$default$" + bfd.name); - bfd.nameOfSetFlag = tdParent.toName(bfd.name + "$set"); - bfd.builderFieldName = tdParent.toName(bfd.name + "$value"); + bfd.nameOfDefaultProvider = parent.toName(DEFAULT_PREFIX + bfd.name); + bfd.nameOfSetFlag = parent.toName(bfd.name + SET_PREFIX); + bfd.builderFieldName = parent.toName(bfd.name + VALUE_PREFIX); JCMethodDecl md = HandleBuilder.generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode, td.typarams); - recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); - if (md != null) injectMethod(tdParent, md); + recursiveSetGeneratedBy(md, annotationNode); + if (md != null) injectMethod(parent, md); } addObtainVia(bfd, fieldNode); - builderFields.add(bfd); - allFields.append(fieldNode); + job.builderFields.add(bfd); } - // Set the names of the builder classes. - String builderClassNameTemplate = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME); - if (builderClassNameTemplate == null || builderClassNameTemplate.isEmpty()) builderClassNameTemplate = "*Builder"; - String builderClassName = builderClassNameTemplate.replace("*", td.name.toString()); - String builderImplClassName = builderClassName + "Impl"; - JCTree extendsClause = Javac.getExtendsClause(td); - JCExpression superclassBuilderClassExpression = null; - if (extendsClause instanceof JCTypeApply) { - // Remember the type arguments, because we need them for the extends clause of our abstract builder class. - superclassTypeParams = ((JCTypeApply) extendsClause).getTypeArguments(); - // A class name with a generics type, e.g., "Superclass<A>". - extendsClause = ((JCTypeApply) extendsClause).getType(); - } - if (extendsClause instanceof JCFieldAccess) { - Name superclassClassName = ((JCFieldAccess)extendsClause).getIdentifier(); - String superclassBuilderClassName = builderClassNameTemplate.replace("*", superclassClassName); - superclassBuilderClassExpression = tdParent.getTreeMaker().Select((JCFieldAccess) extendsClause, - tdParent.toName(superclassBuilderClassName)); - } else if (extendsClause != null) { - String superclassBuilderClassName = builderClassNameTemplate.replace("*", extendsClause.toString()); - superclassBuilderClassExpression = chainDots(tdParent, extendsClause.toString(), superclassBuilderClassName); - } - // If there is no superclass, superclassBuilderClassExpression is still == null at this point. - // You can use it to check whether to inherit or not. - - typeParams = td.typarams; + job.typeParams = job.builderTypeParams = td.typarams; + job.builderClassName = job.replaceBuilderClassName(td.name); + if (!checkName("builderClassName", job.builderClassName, annotationNode)) return; // <C, B> are the generics for our builder. String classGenericName = "C"; @@ -214,14 +222,46 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // We have to make sure that the generics' names do not collide with any generics on the annotated class, // the classname itself, or any member type name of the annotated class. // For instance, if there are generics <B, B2, C> on the annotated class, use "C2" and "B3" for our builder. - java.util.HashSet<String> usedNames = gatherUsedTypeNames(typeParams, td); + java.util.HashSet<String> usedNames = gatherUsedTypeNames(job.typeParams, td); classGenericName = generateNonclashingNameFor(classGenericName, usedNames); builderGenericName = generateNonclashingNameFor(builderGenericName, usedNames); - thrownExceptions = List.nil(); + JavacTreeMaker maker = annotationNode.getTreeMaker(); + + { + JCExpression annotatedClass = namePlusTypeParamsToTypeReference(maker, parent, job.typeParams); + JCTypeParameter c = maker.TypeParameter(parent.toName(classGenericName), List.<JCExpression>of(annotatedClass)); + ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(job.typeParams, maker, job.sourceNode); + typeParamsForBuilder.append(maker.Ident(parent.toName(classGenericName))); + typeParamsForBuilder.append(maker.Ident(parent.toName(builderGenericName))); + JCTypeApply typeApply = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, parent, job.getBuilderClassName(), false, List.<JCTypeParameter>nil()), typeParamsForBuilder.toList()); + JCTypeParameter d = maker.TypeParameter(parent.toName(builderGenericName), List.<JCExpression>of(typeApply)); + if (job.typeParams == null || job.typeParams.isEmpty()) { + job.builderTypeParams_ = List.of(c, d); + } else { + job.builderTypeParams_ = job.typeParams.append(c).append(d); + } + } + + JCTree extendsClause = Javac.getExtendsClause(td); + JCExpression superclassBuilderClass = null; + if (extendsClause instanceof JCTypeApply) { + // Remember the type arguments, because we need them for the extends clause of our abstract builder class. + superclassTypeParams = ((JCTypeApply) extendsClause).getTypeArguments(); + // A class name with a generics type, e.g., "Superclass<A>". + extendsClause = ((JCTypeApply) extendsClause).getType(); + } + if (extendsClause instanceof JCFieldAccess) { + Name superclassName = ((JCFieldAccess) extendsClause).getIdentifier(); + String superclassBuilderClassName = superclassName.toString() + "Builder"; + superclassBuilderClass = parent.getTreeMaker().Select((JCFieldAccess) extendsClause, parent.toName(superclassBuilderClassName)); + } else if (extendsClause != null) { + String superclassBuilderClassName = extendsClause.toString() + "Builder"; + superclassBuilderClass = chainDots(parent, extendsClause.toString(), superclassBuilderClassName); + } // Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary. - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { if (bfd.singularData.getSingularizer().requiresCleaning()) { addCleaning = true; @@ -240,83 +280,85 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { } } + job.builderAbstractClassName = job.builderClassName = job.replaceBuilderClassName(td.name); + job.builderImplClassName = job.builderAbstractClassName + "Impl"; + // Create the abstract builder class. - JavacNode builderType = findInnerClass(tdParent, builderClassName); - if (builderType == null) { - builderType = generateBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression, - typeParams, superclassTypeParams, classGenericName, builderGenericName); - recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); + job.builderAbstractType = findInnerClass(parent, job.builderClassName); + if (job.builderAbstractType == null) { + job.builderAbstractType = generateBuilderAbstractClass(job, superclassBuilderClass, superclassTypeParams, classGenericName, builderGenericName); + recursiveSetGeneratedBy(job.builderAbstractType.get(), annotationNode); } else { - JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get(); + JCClassDecl builderTypeDeclaration = (JCClassDecl) job.builderAbstractType.get(); if (!builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC) || !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.ABSTRACT)) { annotationNode.addError("Existing Builder must be an abstract static inner class."); return; } - sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); + sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(job.builderAbstractType, annotationNode); // Generate errors for @Singular BFDs that have one already defined node. - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { SingularData sd = bfd.singularData; if (sd == null) continue; JavacSingularizer singularizer = sd.getSingularizer(); if (singularizer == null) continue; - if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) { + if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderAbstractType, sd)) { bfd.singularData = null; } } } // Generate the fields in the abstract builder class that hold the values for the instance. - generateBuilderFields(builderType, builderFields, ast); + job.setBuilderToAbstract(); + generateBuilderFields(job.builderType, job.builderFields, annotationNode); if (addCleaning) { - JavacTreeMaker maker = builderType.getTreeMaker(); - JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null); - recursiveSetGeneratedBy(uncleanField, ast, annotationNode.getContext()); - injectFieldAndMarkGenerated(builderType, uncleanField); + JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), job.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null); + recursiveSetGeneratedBy(uncleanField, annotationNode); + injectFieldAndMarkGenerated(job.builderType, uncleanField); } - if (toBuilder) { + if (job.toBuilder) { // Generate $fillValuesFrom() method in the abstract builder. - JCMethodDecl fvm = generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderClassName); - recursiveSetGeneratedBy(fvm, ast, annotationNode.getContext()); - injectMethod(builderType, fvm); + JCMethodDecl fvm = generateFillValuesMethod(job, superclassBuilderClass != null, builderGenericName, classGenericName); + recursiveSetGeneratedBy(fvm, annotationNode); + injectMethod(job.builderType, fvm); // Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class. - JCMethodDecl sfvm = generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, superbuilderAnnotation.setterPrefix()); - recursiveSetGeneratedBy(sfvm, ast, annotationNode.getContext()); - injectMethod(builderType, sfvm); + JCMethodDecl sfvm = generateStaticFillValuesMethod(job, annInstance.setterPrefix()); + recursiveSetGeneratedBy(sfvm, annotationNode); + injectMethod(job.builderType, sfvm); } // Generate abstract self() and build() methods in the abstract builder. - JCMethodDecl asm = generateAbstractSelfMethod(cfv, tdParent, superclassBuilderClassExpression != null, builderGenericName); - recursiveSetGeneratedBy(asm, ast, annotationNode.getContext()); - injectMethod(builderType, asm); - JCMethodDecl abm = generateAbstractBuildMethod(cfv, tdParent, buildMethodName, builderFields, superclassBuilderClassExpression != null, classGenericName); - recursiveSetGeneratedBy(abm, ast, annotationNode.getContext()); - injectMethod(builderType, abm); + JCMethodDecl asm = generateAbstractSelfMethod(job, superclassBuilderClass != null, builderGenericName); + recursiveSetGeneratedBy(asm, annotationNode); + injectMethod(job.builderType, asm); + JCMethodDecl abm = generateAbstractBuildMethod(job, superclassBuilderClass != null, classGenericName); + recursiveSetGeneratedBy(abm, annotationNode); + injectMethod(job.builderType, abm); // Create the setter methods in the abstract builder. - for (BuilderFieldData bfd : builderFields) { - generateSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, builderGenericName, superbuilderAnnotation.setterPrefix()); + for (BuilderFieldData bfd : job.builderFields) { + generateSetterMethodsForBuilder(job, bfd, builderGenericName, annInstance.setterPrefix()); } // Create the toString() method for the abstract builder. java.util.List<Included<JavacNode, ToString.Include>> fieldNodes = new ArrayList<Included<JavacNode, ToString.Include>>(); - for (BuilderFieldData bfd : builderFields) { + for (BuilderFieldData bfd : job.builderFields) { for (JavacNode f : bfd.createdFields) { fieldNodes.add(new Included<JavacNode, ToString.Include>(f, null, true, false)); } } // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder. - JCMethodDecl toStringMethod = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClassExpression != null, FieldAccess.ALWAYS_FIELD, ast); - if (toStringMethod != null) injectMethod(builderType, toStringMethod); + JCMethodDecl toStringMethod = HandleToString.createToString(job.builderType, fieldNodes, true, superclassBuilderClass != null, FieldAccess.ALWAYS_FIELD, annotationNode); + if (toStringMethod != null) injectMethod(job.builderType, toStringMethod); // If clean methods are requested, add them now. if (addCleaning) { - JCMethodDecl md = generateCleanMethod(builderFields, builderType, ast); - recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); - injectMethod(builderType, md); + JCMethodDecl md = generateCleanMethod(job.builderFields, job.builderType, annotationNode); + recursiveSetGeneratedBy(md, annotationNode); + injectMethod(job.builderType, md); } boolean isAbstract = (td.mods.flags & Flags.ABSTRACT) != 0; @@ -324,69 +366,71 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // Only non-abstract classes get the Builder implementation. // Create the builder implementation class. - JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); - if (builderImplType == null) { - builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams); - recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); + job.builderImplType = findInnerClass(parent, job.builderImplClassName); + if (job.builderImplType == null) { + job.builderImplType = generateBuilderImplClass(job); + recursiveSetGeneratedBy(job.builderImplType.get(), annotationNode); } else { - JCClassDecl builderImplTypeDeclaration = (JCClassDecl) builderImplType.get(); + JCClassDecl builderImplTypeDeclaration = (JCClassDecl) job.builderImplType.get(); if (!builderImplTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC) || builderImplTypeDeclaration.getModifiers().getFlags().contains(Modifier.ABSTRACT)) { annotationNode.addError("Existing BuilderImpl must be a non-abstract static inner class."); return; } - sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderImplType, annotationNode); + sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(job.builderImplType, annotationNode); } // Create a simple constructor for the BuilderImpl class. - JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), builderImplType, List.<JavacNode>nil(), false, annotationNode); - if (cd != null) injectMethod(builderImplType, cd); + JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), job.builderImplType, List.<JavacNode>nil(), false, annotationNode); + if (cd != null) injectMethod(job.builderImplType, cd); + job.setBuilderToImpl(); // Create the self() and build() methods in the BuilderImpl. - JCMethodDecl selfMethod = generateSelfMethod(cfv, builderImplType, typeParams); - recursiveSetGeneratedBy(selfMethod, ast, annotationNode.getContext()); - injectMethod(builderImplType, selfMethod); - if (methodExists(buildMethodName, builderImplType, -1) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl buildMethod = generateBuildMethod(cfv, buildMethodName, tdParent, builderImplType, builderFields, thrownExceptions); - recursiveSetGeneratedBy(buildMethod, ast, annotationNode.getContext()); - injectMethod(builderImplType, buildMethod); + JCMethodDecl selfMethod = generateSelfMethod(job); + recursiveSetGeneratedBy(selfMethod, annotationNode); + injectMethod(job.builderType, selfMethod); + if (methodExists(job.buildMethodName, job.builderType, -1) == MemberExistsResult.NOT_EXISTS) { + JCMethodDecl buildMethod = generateBuildMethod(job, buildMethodThrownExceptions); + recursiveSetGeneratedBy(buildMethod, annotationNode); + injectMethod(job.builderType, buildMethod); } } // Generate a constructor in the annotated class that takes a builder as argument. - if (!constructorExists(tdParent, builderClassName)) { - generateBuilderBasedConstructor(cfv, tdParent, typeParams, builderFields, annotationNode, builderClassName, - superclassBuilderClassExpression != null); + if (!constructorExists(job.parentType, job.builderAbstractClassName)) { + job.setBuilderToAbstract(); + generateBuilderBasedConstructor(job, superclassBuilderClass != null); } - if (isAbstract) { + if (!isAbstract) { // Only non-abstract classes get the builder() and toBuilder() methods. - return; - } - // Add the builder() method to the annotated class. - // Allow users to specify their own builder() methods, e.g., to provide default values. - if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; - if (generateBuilderMethod) { - JCMethodDecl builderMethod = generateBuilderMethod(cfv, builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); - recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext()); - if (builderMethod != null) injectMethod(tdParent, builderMethod); - } - - // Add the toBuilder() method to the annotated class. - if (toBuilder) { - switch (methodExists(TO_BUILDER_METHOD_NAME, tdParent, 0)) { - case EXISTS_BY_USER: - break; - case NOT_EXISTS: - JCMethodDecl md = generateToBuilderMethod(cfv, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); - if (md != null) { - recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); - injectMethod(tdParent, md); + // Add the builder() method to the annotated class. + // Allow users to specify their own builder() methods, e.g., to provide default values. + if (generateBuilderMethod && methodExists(job.builderMethodName, job.parentType, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; + if (generateBuilderMethod) { + JCMethodDecl builderMethod = generateBuilderMethod(job); + if (builderMethod != null) { + recursiveSetGeneratedBy(builderMethod, annotationNode); + injectMethod(job.parentType, builderMethod); + } + } + + // Add the toBuilder() method to the annotated class. + if (job.toBuilder) { + switch (methodExists(TO_BUILDER_METHOD_NAME, job.parentType, 0)) { + case EXISTS_BY_USER: + break; + case NOT_EXISTS: + JCMethodDecl md = generateToBuilderMethod(job); + if (md != null) { + recursiveSetGeneratedBy(md, annotationNode); + injectMethod(job.parentType, md); + } + break; + default: + // Should not happen. } - break; - default: - // Should not happen. } } @@ -400,163 +444,150 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { /** * Creates and returns the abstract builder class and injects it into the annotated class. */ - private JavacNode generateBuilderAbstractClass(JavacNode source, JavacNode tdParent, String builderClass, - JCExpression superclassBuilderClassExpression, List<JCTypeParameter> typeParams, - List<JCExpression> superclassTypeParams, String classGenericName, String builderGenericName) { - - JavacTreeMaker maker = tdParent.getTreeMaker(); + private JavacNode generateBuilderAbstractClass(SuperBuilderJob job, JCExpression superclassBuilderClass, List<JCExpression> superclassTypeParams, String classGenericName, String builderGenericName) { + JavacTreeMaker maker = job.parentType.getTreeMaker(); JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC); // Keep any type params of the annotated class. ListBuffer<JCTypeParameter> allTypeParams = new ListBuffer<JCTypeParameter>(); - allTypeParams.addAll(copyTypeParams(source, typeParams)); + allTypeParams.appendList(copyTypeParams(job.sourceNode, job.typeParams)); // Add builder-specific type params required for inheritable builders. // 1. The return type for the build() method, named "C", which extends the annotated class. - JCExpression annotatedClass = namePlusTypeParamsToTypeReference(maker, tdParent, typeParams); + JCExpression annotatedClass = namePlusTypeParamsToTypeReference(maker, job.parentType, job.typeParams); - allTypeParams.add(maker.TypeParameter(tdParent.toName(classGenericName), List.<JCExpression>of(annotatedClass))); + allTypeParams.append(maker.TypeParameter(job.toName(classGenericName), List.<JCExpression>of(annotatedClass))); // 2. The return type for all setter methods, named "B", which extends this builder class. - Name builderClassName = tdParent.toName(builderClass); - ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(typeParams, maker, source.getContext()); - typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); - typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName))); - JCTypeApply typeApply = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, tdParent, builderClassName, false, List.<JCTypeParameter>nil()), typeParamsForBuilder.toList()); - allTypeParams.add(maker.TypeParameter(tdParent.toName(builderGenericName), List.<JCExpression>of(typeApply))); + Name builderClassName = job.toName(job.builderClassName); + ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(job.typeParams, maker, job.sourceNode); + typeParamsForBuilder.append(maker.Ident(job.toName(classGenericName))); + typeParamsForBuilder.append(maker.Ident(job.toName(builderGenericName))); + JCTypeApply typeApply = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, job.parentType, builderClassName, false, List.<JCTypeParameter>nil()), typeParamsForBuilder.toList()); + allTypeParams.append(maker.TypeParameter(job.toName(builderGenericName), List.<JCExpression>of(typeApply))); JCExpression extending = null; - if (superclassBuilderClassExpression != null) { + if (superclassBuilderClass != null) { // If the annotated class extends another class, we want this builder to extend the builder of the superclass. // 1. Add the type parameters of the superclass. - typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker, source.getContext()); + typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker, job.sourceNode); // 2. Add the builder type params <C, B>. - typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName))); - typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName))); - extending = maker.TypeApply(superclassBuilderClassExpression, typeParamsForBuilder.toList()); + typeParamsForBuilder.append(maker.Ident(job.toName(classGenericName))); + typeParamsForBuilder.append(maker.Ident(job.toName(builderGenericName))); + extending = maker.TypeApply(superclassBuilderClass, typeParamsForBuilder.toList()); } JCClassDecl builder = maker.ClassDef(mods, builderClassName, allTypeParams.toList(), extending, List.<JCExpression>nil(), List.<JCTree>nil()); - return injectType(tdParent, builder); + recursiveSetGeneratedBy(builder, job.sourceNode); + return injectType(job.parentType, builder); } /** * Creates and returns the concrete builder implementation class and injects it into the annotated class. */ - private JavacNode generateBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List<JCTypeParameter> typeParams) { - JavacTreeMaker maker = tdParent.getTreeMaker(); + private JavacNode generateBuilderImplClass(SuperBuilderJob job) { + JavacTreeMaker maker = job.getTreeMaker(); JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE | Flags.FINAL); // Extend the abstract builder. - JCExpression extending = namePlusTypeParamsToTypeReference(maker, tdParent, tdParent.toName(builderAbstractClass), false, List.<JCTypeParameter>nil()); - // Add any type params of the annotated class. - ListBuffer<JCTypeParameter> allTypeParams = new ListBuffer<JCTypeParameter>(); - allTypeParams.addAll(copyTypeParams(source, typeParams)); + JCExpression extending = namePlusTypeParamsToTypeReference(maker, job.parentType, job.toName(job.builderAbstractClassName), false, List.<JCTypeParameter>nil()); + // Add builder-specific type params required for inheritable builders. // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class. - JCExpression annotatedClass = namePlusTypeParamsToTypeReference(maker, tdParent, typeParams); + JCExpression annotatedClass = namePlusTypeParamsToTypeReference(maker, job.parentType, job.typeParams); // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class. - JCExpression builderImplClassExpression = namePlusTypeParamsToTypeReference(maker, tdParent, tdParent.toName(builderImplClass), false, typeParams); + JCExpression builderImplClassExpression = namePlusTypeParamsToTypeReference(maker, job.parentType, job.toName(job.builderImplClassName), false, job.typeParams); - ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(typeParams, maker, source.getContext()); - typeParamsForBuilder.add(annotatedClass); - typeParamsForBuilder.add(builderImplClassExpression); + ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(job.typeParams, maker, job.sourceNode); + typeParamsForBuilder.append(annotatedClass); + typeParamsForBuilder.append(builderImplClassExpression); extending = maker.TypeApply(extending, typeParamsForBuilder.toList()); - JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderImplClass), copyTypeParams(source, typeParams), extending, List.<JCExpression>nil(), List.<JCTree>nil()); - return injectType(tdParent, builder); + JCClassDecl builder = maker.ClassDef(mods, job.toName(job.builderImplClassName), copyTypeParams(job.parentType, job.typeParams), extending, List.<JCExpression>nil(), List.<JCTree>nil()); + recursiveSetGeneratedBy(builder, job.sourceNode); + return injectType(job.parentType, builder); } /** * Generates a constructor that has a builder as the only parameter. * The values from the builder are used to initialize the fields of new instances. * - * @param typeNode - * the type (with the {@code @Builder} annotation) for which a - * constructor should be generated. - * @param typeParams - * @param builderFields a list of fields in the builder which should be assigned to new instances. - * @param source the annotation (used for setting source code locations for the generated code). * @param callBuilderBasedSuperConstructor * If {@code true}, the constructor will explicitly call a super * constructor with the builder as argument. Requires * {@code builderClassAsParameter != null}. */ - private void generateBuilderBasedConstructor(CheckerFrameworkVersion cfv, JavacNode typeNode, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, JavacNode source, String builderClassName, boolean callBuilderBasedSuperConstructor) { - JavacTreeMaker maker = typeNode.getTreeMaker(); + private void generateBuilderBasedConstructor(SuperBuilderJob job, boolean callBuilderBasedSuperConstructor) { + JavacTreeMaker maker = job.getTreeMaker(); AccessLevel level = AccessLevel.PROTECTED; ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); - Name builderVariableName = typeNode.toName(BUILDER_VARIABLE_NAME); - for (BuilderFieldData bfd : builderFields) { + Name builderVariableName = job.toName(BUILDER_VARIABLE_NAME); + for (BuilderFieldData bfd : job.builderFields) { JCExpression rhs; if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.originalFieldNode, bfd.type, statements, bfd.builderFieldName, "b"); + bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.originalFieldNode, job.sourceNode, statements, bfd.builderFieldName, "b"); rhs = maker.Ident(bfd.singularData.getPluralName()); } else { rhs = maker.Select(maker.Ident(builderVariableName), bfd.builderFieldName); } - JCFieldAccess fieldInThis = maker.Select(maker.Ident(typeNode.toName("this")), bfd.rawName); + JCFieldAccess fieldInThis = maker.Select(maker.Ident(job.toName("this")), bfd.rawName); JCStatement assign = maker.Exec(maker.Assign(fieldInThis, rhs)); // In case of @Builder.Default, set the value to the default if it was not set in the builder. if (bfd.nameOfSetFlag != null) { JCFieldAccess setField = maker.Select(maker.Ident(builderVariableName), bfd.nameOfSetFlag); - fieldInThis = maker.Select(maker.Ident(typeNode.toName("this")), bfd.rawName); - JCExpression parentTypeRef = namePlusTypeParamsToTypeReference(maker, typeNode, List.<JCTypeParameter>nil()); - JCAssign assignDefault = maker.Assign(fieldInThis, maker.Apply(typeParameterNames(maker, ((JCClassDecl) typeNode.get()).typarams), maker.Select(parentTypeRef, bfd.nameOfDefaultProvider), List.<JCExpression>nil())); + fieldInThis = maker.Select(maker.Ident(job.toName("this")), bfd.rawName); + JCExpression parentTypeRef = namePlusTypeParamsToTypeReference(maker, job.parentType, List.<JCTypeParameter>nil()); + JCAssign assignDefault = maker.Assign(fieldInThis, maker.Apply(typeParameterNames(maker, ((JCClassDecl) job.parentType.get()).typarams), maker.Select(parentTypeRef, bfd.nameOfDefaultProvider), List.<JCExpression>nil())); statements.append(maker.If(setField, assign, maker.Exec(assignDefault))); } else { statements.append(assign); } if (hasNonNullAnnotations(bfd.originalFieldNode)) { - JCStatement nullCheck = generateNullCheck(maker, bfd.originalFieldNode, source); + JCStatement nullCheck = generateNullCheck(maker, bfd.originalFieldNode, job.sourceNode); if (nullCheck != null) statements.append(nullCheck); } } - List<JCAnnotation> annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil(); + List<JCAnnotation> annsOnMethod = job.checkerFramework.generateSideEffectFree() ? List.of(maker.Annotation(genTypeRef(job.parentType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil(); JCModifiers mods = maker.Modifiers(toJavacModifier(level), annsOnMethod); // Create a constructor that has just the builder as parameter. ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); - long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); - Name builderClassname = typeNode.toName(builderClassName); + long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, job.getContext()); // First add all generics that are present on the parent type. - ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(typeParams, maker, typeNode.getContext()); + ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(job.typeParams, maker, job.sourceNode); // Now add the <?, ?>. JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); - typeParamsForBuilderParameter.add(wildcard); + typeParamsForBuilderParameter.append(wildcard); wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); - typeParamsForBuilderParameter.add(wildcard); - JCTypeApply paramType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, typeNode, builderClassname, false, List.<JCTypeParameter>nil()), typeParamsForBuilderParameter.toList()); + typeParamsForBuilderParameter.append(wildcard); + JCTypeApply paramType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, job.parentType, job.getBuilderClassName(), false, List.<JCTypeParameter>nil()), typeParamsForBuilderParameter.toList()); JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, paramType, null); params.append(param); if (callBuilderBasedSuperConstructor) { // The first statement must be the call to the super constructor. JCMethodInvocation callToSuperConstructor = maker.Apply(List.<JCExpression>nil(), - maker.Ident(typeNode.toName("super")), + maker.Ident(job.toName("super")), List.<JCExpression>of(maker.Ident(builderVariableName))); statements.prepend(maker.Exec(callToSuperConstructor)); } - JCMethodDecl constr = recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"), + JCMethodDecl constr = recursiveSetGeneratedBy(maker.MethodDef(mods, job.toName("<init>"), null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), - maker.Block(0L, statements.toList()), null), source.get(), typeNode.getContext()); + maker.Block(0L, statements.toList()), null), job.sourceNode); - injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); + injectMethod(job.parentType, constr, null, Javac.createVoidType(job.builderType.getSymbolTable(), CTC_VOID)); } - private JCMethodDecl generateBuilderMethod(CheckerFrameworkVersion cfv, String builderMethodName, String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) { - JavacTreeMaker maker = type.getTreeMaker(); - - ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); - for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name)); + private JCMethodDecl generateBuilderMethod(SuperBuilderJob job) { + JavacTreeMaker maker = job.getTreeMaker(); - JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type, type.toName(builderImplClassName), false, typeParams), List.<JCExpression>nil(), null); + JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, job.parentType, job.toName(job.builderImplClassName), false, job.typeParams), List.<JCExpression>nil(), null); JCStatement statement = maker.Return(call); JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); @@ -565,16 +596,19 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // Add any type params of the annotated class to the return type. ListBuffer<JCExpression> typeParameterNames = new ListBuffer<JCExpression>(); - typeParameterNames.addAll(typeParameterNames(maker, typeParams)); + typeParameterNames.appendList(typeParameterNames(maker, job.typeParams)); // Now add the <?, ?>. JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); - typeParameterNames.add(wildcard); - typeParameterNames.add(wildcard); - JCTypeApply returnType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), false, List.<JCTypeParameter>nil()), typeParameterNames.toList()); - - List<JCAnnotation> annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil(); - JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); - createRelevantNonNullAnnotation(type, methodDef); + typeParameterNames.append(wildcard); + typeParameterNames.append(wildcard); + // And return type annotations. + List<JCAnnotation> annsOnParamType = List.nil(); + if (job.checkerFramework.generateUnique()) annsOnParamType = List.of(maker.Annotation(genTypeRef(job.parentType, CheckerFrameworkVersion.NAME__UNIQUE), List.<JCExpression>nil())); + JCTypeApply returnType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, job.parentType, job.toName(job.builderAbstractClassName), false, List.<JCTypeParameter>nil(), annsOnParamType), typeParameterNames.toList()); + + List<JCAnnotation> annsOnMethod = job.checkerFramework.generateSideEffectFree() ? List.of(maker.Annotation(genTypeRef(job.parentType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil(); + JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), job.toName(job.builderMethodName), returnType, copyTypeParams(job.sourceNode, job.typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); + createRelevantNonNullAnnotation(job.parentType, methodDef); return methodDef; } @@ -586,15 +620,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { * } * </pre> */ - private JCMethodDecl generateToBuilderMethod(CheckerFrameworkVersion cfv, String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) { - JavacTreeMaker maker = type.getTreeMaker(); + private JCMethodDecl generateToBuilderMethod(SuperBuilderJob job) { + JavacTreeMaker maker = job.getTreeMaker(); - ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); - for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name)); - - JCExpression newClass = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type, type.toName(builderImplClassName), false, typeParams), List.<JCExpression>nil(), null); - List<JCExpression> methodArgs = List.<JCExpression>of(maker.Ident(type.toName("this"))); - JCMethodInvocation invokeFillMethod = maker.Apply(List.<JCExpression>nil(), maker.Select(newClass, type.toName(FILL_VALUES_METHOD_NAME)), methodArgs); + JCExpression newClass = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, job.parentType, job.toName(job.builderImplClassName), false, job.typeParams), List.<JCExpression>nil(), null); + List<JCExpression> methodArgs = List.<JCExpression>of(maker.Ident(job.toName("this"))); + JCMethodInvocation invokeFillMethod = maker.Apply(List.<JCExpression>nil(), maker.Select(newClass, job.toName(FILL_VALUES_METHOD_NAME)), methodArgs); JCStatement statement = maker.Return(invokeFillMethod); JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); @@ -602,16 +633,16 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // Add any type params of the annotated class to the return type. ListBuffer<JCExpression> typeParameterNames = new ListBuffer<JCExpression>(); - typeParameterNames.addAll(typeParameterNames(maker, typeParams)); + typeParameterNames.appendList(typeParameterNames(maker, job.typeParams)); // Now add the <?, ?>. JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); - typeParameterNames.add(wildcard); - typeParameterNames.add(wildcard); - JCTypeApply returnType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassName), false, List.<JCTypeParameter>nil()), typeParameterNames.toList()); + typeParameterNames.append(wildcard); + typeParameterNames.append(wildcard); + JCTypeApply returnType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, job.parentType, job.toName(job.builderAbstractClassName), false, List.<JCTypeParameter>nil()), typeParameterNames.toList()); - List<JCAnnotation> annsOnMethod = cfv.generateUnique() ? List.of(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil(); - JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), type.toName(TO_BUILDER_METHOD_NAME), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); - createRelevantNonNullAnnotation(type, methodDef); + List<JCAnnotation> annsOnMethod = job.checkerFramework.generateSideEffectFree() ? List.of(maker.Annotation(genTypeRef(job.parentType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil(); + JCMethodDecl methodDef = maker.MethodDef(maker.Modifiers(modifiers, annsOnMethod), job.toName(TO_BUILDER_METHOD_NAME), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); + createRelevantNonNullAnnotation(job.parentType, methodDef); return methodDef; } @@ -621,43 +652,43 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { * <pre> * protected B $fillValuesFrom(final C instance) { * super.$fillValuesFrom(instance); - * FoobarBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this); + * FoobarBuilder.$fillValuesFromInstanceIntoBuilder(instance, this); * return self(); * } * </pre> */ - private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited, String builderGenericName, String classGenericName, String builderImplClassName) { - JavacTreeMaker maker = type.getTreeMaker(); + private JCMethodDecl generateFillValuesMethod(SuperBuilderJob job, boolean inherited, String builderGenericName, String classGenericName) { + JavacTreeMaker maker = job.getTreeMaker(); List<JCAnnotation> annotations = List.nil(); if (inherited) { - JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil()); + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(job.builderType, "Override"), List.<JCExpression>nil()); annotations = List.of(overrideAnnotation); } JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED, annotations); - Name name = type.toName(FILL_VALUES_METHOD_NAME); - JCExpression returnType = maker.Ident(type.toName(builderGenericName)); + Name name = job.toName(FILL_VALUES_METHOD_NAME); + JCExpression returnType = maker.Ident(job.toName(builderGenericName)); - JCExpression classGenericNameExpr = maker.Ident(type.toName(classGenericName)); - JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(INSTANCE_VARIABLE_NAME), classGenericNameExpr, null); + JCExpression classGenericNameExpr = maker.Ident(job.toName(classGenericName)); + JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.PARAMETER | Flags.FINAL), job.toName(INSTANCE_VARIABLE_NAME), classGenericNameExpr, null); ListBuffer<JCStatement> body = new ListBuffer<JCStatement>(); if (inherited) { // Call super. JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(), - maker.Select(maker.Ident(type.toName("super")), name), - List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)))); + maker.Select(maker.Ident(job.toName("super")), name), + List.<JCExpression>of(maker.Ident(job.toName(INSTANCE_VARIABLE_NAME)))); body.append(maker.Exec(callToSuper)); } // Call the builder implemention's helper method that actually fills the values from the instance. - JCExpression ref = namePlusTypeParamsToTypeReference(maker, type, type.toName(builderImplClassName), false, List.<JCTypeParameter>nil()); + JCExpression ref = namePlusTypeParamsToTypeReference(maker, job.parentType, job.getBuilderClassName(), false, List.<JCTypeParameter>nil()); JCMethodInvocation callStaticFillValuesMethod = maker.Apply(List.<JCExpression>nil(), - maker.Select(ref, type.toName(STATIC_FILL_VALUES_METHOD_NAME)), - List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), maker.Ident(type.toName("this")))); + maker.Select(ref, job.toName(STATIC_FILL_VALUES_METHOD_NAME)), + List.<JCExpression>of(maker.Ident(job.toName(INSTANCE_VARIABLE_NAME)), maker.Ident(job.toName("this")))); body.append(maker.Exec(callStaticFillValuesMethod)); - JCReturn returnStatement = maker.Return(maker.Apply(List.<JCExpression>nil(), maker.Ident(type.toName(SELF_METHOD)), List.<JCExpression>nil())); + JCReturn returnStatement = maker.Return(maker.Apply(List.<JCExpression>nil(), maker.Ident(job.toName(SELF_METHOD)), List.<JCExpression>nil())); body.append(returnStatement); JCBlock bodyBlock = maker.Block(0, body.toList()); @@ -674,58 +705,58 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { * b.field(instance.field); * } * </pre> - * @param setterPrefix the prefix for setter methods */ - private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, String builderClassname, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, String setterPrefix) { - JavacTreeMaker maker = type.getTreeMaker(); + private JCMethodDecl generateStaticFillValuesMethod(SuperBuilderJob job, String setterPrefix) { + JavacTreeMaker maker = job.getTreeMaker(); List<JCAnnotation> annotations = List.nil(); JCModifiers modifiers = maker.Modifiers(Flags.PRIVATE | Flags.STATIC, annotations); - Name name = type.toName(STATIC_FILL_VALUES_METHOD_NAME); + Name name = job.toName(STATIC_FILL_VALUES_METHOD_NAME); JCExpression returnType = maker.TypeIdent(CTC_VOID); // 1st parameter: "Foobar instance" - JCVariableDecl paramInstance = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(INSTANCE_VARIABLE_NAME), cloneSelfType(type), null); + JCVariableDecl paramInstance = maker.VarDef(maker.Modifiers(Flags.PARAMETER | Flags.FINAL), job.toName(INSTANCE_VARIABLE_NAME), cloneSelfType(job.parentType), null); // 2nd parameter: "FoobarBuilder<?, ?> b" (plus generics on the annotated type) // First add all generics that are present on the parent type. - ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(typeParams, maker, type.getContext()); + ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(job.typeParams, maker, job.sourceNode); // Now add the <?, ?>. JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); - typeParamsForBuilderParameter.add(wildcard); + typeParamsForBuilderParameter.append(wildcard); wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); - typeParamsForBuilderParameter.add(wildcard); - JCTypeApply builderType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, type, type.toName(builderClassname), false, List.<JCTypeParameter>nil()), typeParamsForBuilderParameter.toList()); - JCVariableDecl paramBuilder = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(BUILDER_VARIABLE_NAME), builderType, null); - + typeParamsForBuilderParameter.append(wildcard); + JCTypeApply builderType = maker.TypeApply(namePlusTypeParamsToTypeReference(maker, job.parentType, job.getBuilderClassName(), false, List.<JCTypeParameter>nil()), typeParamsForBuilderParameter.toList()); + JCVariableDecl paramBuilder = maker.VarDef(maker.Modifiers(Flags.PARAMETER | Flags.FINAL), job.toName(BUILDER_VARIABLE_NAME), builderType, null); + ListBuffer<JCStatement> body = new ListBuffer<JCStatement>(); // Call the builder's setter methods to fill the values from the instance. - for (BuilderFieldData bfd : builderFields) { - JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, type, maker, setterPrefix); + for (BuilderFieldData bfd : job.builderFields) { + JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, job, setterPrefix); body.append(exec); } JCBlock bodyBlock = maker.Block(0, body.toList()); - - return maker.MethodDef(modifiers, name, returnType, copyTypeParams(type, typeParams), List.of(paramInstance, paramBuilder), List.<JCExpression>nil(), bodyBlock, null); + + return maker.MethodDef(modifiers, name, returnType, copyTypeParams(job.builderType, job.typeParams), List.of(paramInstance, paramBuilder), List.<JCExpression>nil(), bodyBlock, null); } - private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData bfd, JavacNode type, JavacTreeMaker maker, String setterPrefix) { + private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData bfd, SuperBuilderJob job, String setterPrefix) { + JavacTreeMaker maker = job.getTreeMaker(); JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2]; if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { for (int i = 0; i < tgt.length; i++) { - tgt[i] = maker.Select(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field())); + tgt[i] = maker.Select(maker.Ident(job.toName(INSTANCE_VARIABLE_NAME)), bfd.obtainVia == null ? bfd.rawName : job.toName(bfd.obtainVia.field())); } } else { if (bfd.obtainVia.isStatic()) { for (int i = 0; i < tgt.length; i++) { - JCExpression typeRef = namePlusTypeParamsToTypeReference(maker, type, List.<JCTypeParameter>nil()); - JCExpression c = maker.Select(typeRef, type.toName(bfd.obtainVia.method())); - tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)))); + JCExpression typeRef = namePlusTypeParamsToTypeReference(maker, job.parentType, List.<JCTypeParameter>nil()); + JCExpression c = maker.Select(typeRef, job.toName(bfd.obtainVia.method())); + tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(job.toName(INSTANCE_VARIABLE_NAME)))); } } else { for (int i = 0; i < tgt.length; i++) { - JCExpression c = maker.Select(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), type.toName(bfd.obtainVia.method())); + JCExpression c = maker.Select(maker.Ident(job.toName(INSTANCE_VARIABLE_NAME)), job.toName(bfd.obtainVia.method())); tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil()); } } @@ -736,95 +767,105 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { arg = tgt[0]; } else { JCExpression eqNull = maker.Binary(CTC_EQUAL, tgt[0], maker.Literal(CTC_BOT, null)); - String emptyMaker = bfd.singularData.getSingularizer().getEmptyMaker(bfd.singularData.getTargetFqn()); - JCExpression emptyCollection = maker.Apply(List.<JCExpression>nil(), chainDots(type, emptyMaker.split("\\.")), List.<JCExpression>nil()); + JCExpression emptyCollection = bfd.singularData.getSingularizer().getEmptyExpression(bfd.singularData.getTargetFqn(), maker, bfd.singularData, job.parentType, job.sourceNode); arg = maker.Conditional(eqNull, emptyCollection, tgt[1]); } String setterName = HandlerUtil.buildAccessorName(setterPrefix, bfd.name.toString()); - JCMethodInvocation apply = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName(BUILDER_VARIABLE_NAME)), type.toName(setterName)), List.of(arg)); + JCMethodInvocation apply = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(job.toName(BUILDER_VARIABLE_NAME)), job.toName(setterName)), List.of(arg)); JCExpressionStatement exec = maker.Exec(apply); return exec; } - private JCMethodDecl generateAbstractSelfMethod(CheckerFrameworkVersion cfv, JavacNode type, boolean override, String builderGenericName) { - JavacTreeMaker maker = type.getTreeMaker(); + private JCMethodDecl generateAbstractSelfMethod(SuperBuilderJob job, boolean override, String builderGenericName) { + JavacTreeMaker maker = job.getTreeMaker(); List<JCAnnotation> annotations = List.nil(); - JCAnnotation overrideAnnotation = override ? maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil()) : null; - JCAnnotation rrAnnotation = cfv.generateReturnsReceiver() ? maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.<JCExpression>nil()) : null; - JCAnnotation sefAnnotation = cfv.generatePure() ? maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__PURE), List.<JCExpression>nil()) : null; + JCAnnotation overrideAnnotation = override ? maker.Annotation(genJavaLangTypeRef(job.builderType, "Override"), List.<JCExpression>nil()) : null; + JCAnnotation rrAnnotation = job.checkerFramework.generateReturnsReceiver() ? maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.<JCExpression>nil()) : null; + JCAnnotation sefAnnotation = job.checkerFramework.generatePure() ? maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__PURE), List.<JCExpression>nil()) : null; if (sefAnnotation != null) annotations = annotations.prepend(sefAnnotation); if (rrAnnotation != null) annotations = annotations.prepend(rrAnnotation); if (overrideAnnotation != null) annotations = annotations.prepend(overrideAnnotation); JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations); - Name name = type.toName(SELF_METHOD); - JCExpression returnType = maker.Ident(type.toName(builderGenericName)); + Name name = job.toName(SELF_METHOD); + JCExpression returnType = maker.Ident(job.toName(builderGenericName)); return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null); } - private JCMethodDecl generateSelfMethod(CheckerFrameworkVersion cfv, JavacNode builderImplType, List<JCTypeParameter> typeParams) { - JavacTreeMaker maker = builderImplType.getTreeMaker(); + private JCMethodDecl generateSelfMethod(SuperBuilderJob job) { + JavacTreeMaker maker = job.getTreeMaker(); - JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(builderImplType, "Override"), List.<JCExpression>nil()); - JCAnnotation rrAnnotation = cfv.generateReturnsReceiver() ? maker.Annotation(genTypeRef(builderImplType, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.<JCExpression>nil()) : null; - JCAnnotation sefAnnotation = cfv.generatePure() ? maker.Annotation(genTypeRef(builderImplType, CheckerFrameworkVersion.NAME__PURE), List.<JCExpression>nil()) : null; + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(job.builderType, "Override"), List.<JCExpression>nil()); + JCAnnotation rrAnnotation = job.checkerFramework.generateReturnsReceiver() ? maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.<JCExpression>nil()) : null; + JCAnnotation sefAnnotation = job.checkerFramework.generatePure() ? maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__PURE), List.<JCExpression>nil()) : null; List<JCAnnotation> annsOnMethod = List.nil(); if (sefAnnotation != null) annsOnMethod = annsOnMethod.prepend(sefAnnotation); if (rrAnnotation != null) annsOnMethod = annsOnMethod.prepend(rrAnnotation); annsOnMethod = annsOnMethod.prepend(overrideAnnotation); JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED, annsOnMethod); - Name name = builderImplType.toName(SELF_METHOD); + Name name = job.toName(SELF_METHOD); - JCExpression returnType = namePlusTypeParamsToTypeReference(maker, builderImplType.up(), builderImplType.toName(builderImplType.getName()), false, typeParams); - JCStatement statement = maker.Return(maker.Ident(builderImplType.toName("this"))); + JCExpression returnType = namePlusTypeParamsToTypeReference(maker, job.builderType.up(), job.getBuilderClassName(), false, job.typeParams); + JCStatement statement = maker.Return(maker.Ident(job.toName("this"))); JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); } - private JCMethodDecl generateAbstractBuildMethod(CheckerFrameworkVersion cfv, JavacNode type, String methodName, java.util.List<BuilderFieldData> builderFields, boolean override, String classGenericName) { - JavacTreeMaker maker = type.getTreeMaker(); + private JCMethodDecl generateAbstractBuildMethod(SuperBuilderJob job, boolean override, String classGenericName) { + JavacTreeMaker maker = job.getTreeMaker(); List<JCAnnotation> annotations = List.nil(); if (override) { - JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil()); + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(job.builderType, "Override"), List.<JCExpression>nil()); annotations = List.of(overrideAnnotation); } - if (cfv.generateSideEffectFree()) annotations = annotations.prepend(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())); + if (job.checkerFramework.generateSideEffectFree()) annotations = annotations.prepend(maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())); JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT, annotations); - Name name = type.toName(methodName); - JCExpression returnType = maker.Ident(type.toName(classGenericName)); + Name name = job.toName(job.buildMethodName); + JCExpression returnType = maker.Ident(job.toName(classGenericName)); - List<JCVariableDecl> params = HandleBuilder.generateBuildArgs(cfv, type, builderFields); - return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), null, null); + JCVariableDecl recv = HandleBuilder.generateReceiver(job); + JCMethodDecl methodDef; + if (recv != null && maker.hasMethodDefWithRecvParam()) { + methodDef = maker.MethodDefWithRecvParam(modifiers, name, returnType, List.<JCTypeParameter>nil(), recv, List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null); + } else { + methodDef = maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null); + } + return methodDef; } - - private JCMethodDecl generateBuildMethod(CheckerFrameworkVersion cfv, String buildName, JavacNode returnType, JavacNode type, java.util.List<BuilderFieldData> builderFields, List<JCExpression> thrownExceptions) { - JavacTreeMaker maker = type.getTreeMaker(); + + private JCMethodDecl generateBuildMethod(SuperBuilderJob job, List<JCExpression> thrownExceptions) { + JavacTreeMaker maker = job.getTreeMaker(); JCExpression call; ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); // Use a constructor that only has this builder as parameter. - List<JCExpression> builderArg = List.<JCExpression>of(maker.Ident(type.toName("this"))); - call = maker.NewClass(null, List.<JCExpression>nil(), cloneSelfType(returnType), builderArg, null); + List<JCExpression> builderArg = List.<JCExpression>of(maker.Ident(job.toName("this"))); + call = maker.NewClass(null, List.<JCExpression>nil(), cloneSelfType(job.parentType), builderArg, null); statements.append(maker.Return(call)); JCBlock body = maker.Block(0, statements.toList()); - JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil()); + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(job.builderType, "Override"), List.<JCExpression>nil()); List<JCAnnotation> annsOnMethod = List.of(overrideAnnotation); - if (cfv.generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(type, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())); + if (job.checkerFramework.generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())); JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, annsOnMethod); - List<JCVariableDecl> params = HandleBuilder.generateBuildArgs(cfv, type, builderFields); - JCMethodDecl methodDef = maker.MethodDef(modifiers, type.toName(buildName), cloneSelfType(returnType), List.<JCTypeParameter>nil(), params, thrownExceptions, body, null); - createRelevantNonNullAnnotation(type, methodDef); + JCVariableDecl recv = HandleBuilder.generateReceiver(job); + JCMethodDecl methodDef; + if (recv != null && maker.hasMethodDefWithRecvParam()) { + methodDef = maker.MethodDefWithRecvParam(modifiers, job.toName(job.buildMethodName), cloneSelfType(job.parentType), List.<JCTypeParameter>nil(), recv, List.<JCVariableDecl>nil(), thrownExceptions, body, null); + } else { + methodDef = maker.MethodDef(modifiers, job.toName(job.buildMethodName), cloneSelfType(job.parentType), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null); + } + createRelevantNonNullAnnotation(job.builderType, methodDef); return methodDef; } - private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) { + private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JavacNode source) { JavacTreeMaker maker = type.getTreeMaker(); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); @@ -839,7 +880,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(type.getSymbolTable(), CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); } - private void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) { + private void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JavacNode source) { int len = builderFields.size(); java.util.List<JavacNode> existing = new ArrayList<JavacNode>(); for (JavacNode child : builderType.down()) { @@ -851,7 +892,11 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { for (int i = len - 1; i >= 0; i--) { BuilderFieldData bfd = builderFields.get(i); if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { - bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source)); + java.util.List<JavacNode> fields = bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source); + for (JavacNode field : fields) { + generated.add((JCVariableDecl) field.get()); + } + bfd.createdFields.addAll(fields); } else { JavacNode field = null, setFlag = null; for (JavacNode exists : existing) { @@ -862,7 +907,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { JavacTreeMaker maker = builderType.getTreeMaker(); if (field == null) { JCModifiers mods = maker.Modifiers(Flags.PRIVATE); - JCVariableDecl newField = maker.VarDef(mods, bfd.builderFieldName, cloneType(maker, bfd.type, source, builderType.getContext()), null); + JCVariableDecl newField = maker.VarDef(mods, bfd.builderFieldName, cloneType(maker, bfd.type, source), null); field = injectFieldAndMarkGenerated(builderType, newField); generated.add(newField); } @@ -875,32 +920,33 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { bfd.createdFields.add(field); } } - for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, source, builderType.getContext()); + for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, source); } - private void generateSetterMethodsForBuilder(CheckerFrameworkVersion cfv, final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName, String setterPrefix) { + private void generateSetterMethodsForBuilder(final SuperBuilderJob job, BuilderFieldData fieldNode, final String builderGenericName, String setterPrefix) { boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); - final JavacTreeMaker maker = builderType.getTreeMaker(); + final JavacTreeMaker maker = job.getTreeMaker(); ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() { - return maker.Ident(builderType.toName(builderGenericName)); + return maker.Ident(job.toName(builderGenericName)); }}; StatementMaker returnStatementMaker = new StatementMaker() { @Override public JCStatement make() { - return maker.Return(maker.Apply(List.<JCExpression>nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.<JCExpression>nil())); + return maker.Return(maker.Apply(List.<JCExpression>nil(), maker.Ident(job.toName(SELF_METHOD)), List.<JCExpression>nil())); }}; if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - generateSimpleSetterMethodForBuilder(cfv, builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, source, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations, fieldNode.originalFieldNode, setterPrefix); + generateSimpleSetterMethodForBuilder(job, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations, fieldNode.originalFieldNode, setterPrefix); } else { - fieldNode.singularData.getSingularizer().generateMethods(cfv, fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC); + fieldNode.singularData.getSingularizer().generateMethods(job.checkerFramework, fieldNode.singularData, deprecate, job.builderType, + job.sourceNode, true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC); } } - private void generateSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, JCExpression returnType, JCStatement returnStatement, List<JCAnnotation> annosOnParam, JavacNode originalFieldNode, String setterPrefix) { + private void generateSimpleSetterMethodForBuilder(SuperBuilderJob job, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JCExpression returnType, JCStatement returnStatement, List<JCAnnotation> annosOnParam, JavacNode originalFieldNode, String setterPrefix) { String setterName = HandlerUtil.buildAccessorName(setterPrefix, paramName.toString()); - Name setterName_ = builderType.toName(setterName); + Name setterName_ = job.builderType.toName(setterName); - for (JavacNode child : builderType.down()) { + for (JavacNode child : job.builderType.down()) { if (child.getKind() != Kind.METHOD) continue; JCMethodDecl methodDecl = (JCMethodDecl) child.get(); Name existingName = methodDecl.name; @@ -910,23 +956,24 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { JavacTreeMaker maker = fieldNode.getTreeMaker(); List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); - JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, returnType, returnStatement, source, methodAnns, annosOnParam); - if (cfv.generateCalledMethods()) { - JCAnnotation ncAnno = maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__NOT_CALLED), List.<JCExpression>of(maker.Literal(newMethod.getName().toString()))); - JCClassDecl builderTypeNode = (JCClassDecl) builderType.get(); - JCExpression selfType = namePlusTypeParamsToTypeReference(maker, builderType, builderTypeNode.typarams); - JCVariableDecl recv = maker.VarDef(maker.Modifiers(0L, List.<JCAnnotation>of(ncAnno)), builderType.toName("this"), selfType, null); - newMethod.params = List.of(recv, newMethod.params.get(0)); + JCMethodDecl newMethod = null; + if (job.checkerFramework.generateCalledMethods() && maker.hasMethodDefWithRecvParam()) { + JCAnnotation ncAnno = maker.Annotation(genTypeRef(job.sourceNode, CheckerFrameworkVersion.NAME__NOT_CALLED), List.<JCExpression>of(maker.Literal(setterName.toString()))); + JCClassDecl builderTypeNode = (JCClassDecl) job.builderType.get(); + JCExpression selfType = namePlusTypeParamsToTypeReference(maker, job.builderType, builderTypeNode.typarams, List.<JCAnnotation>of(ncAnno)); + JCVariableDecl recv = maker.VarDef(maker.Modifiers(0L, List.<JCAnnotation>nil()), job.toName("this"), selfType, null); + newMethod = HandleSetter.createSetterWithRecv(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, returnType, returnStatement, job.sourceNode, methodAnns, annosOnParam, recv); } - if (cfv.generateReturnsReceiver()) { + if (newMethod == null) newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, returnType, returnStatement, job.sourceNode, methodAnns, annosOnParam); + if (job.checkerFramework.generateReturnsReceiver()) { List<JCAnnotation> annotations = newMethod.mods.annotations; if (annotations == null) annotations = List.nil(); - JCAnnotation anno = maker.Annotation(genTypeRef(source, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.<JCExpression>nil()); - recursiveSetGeneratedBy(anno, source.get(), builderType.getContext()); + JCAnnotation anno = maker.Annotation(genTypeRef(job.builderType, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.<JCExpression>nil()); + recursiveSetGeneratedBy(anno, job.sourceNode); newMethod.mods.annotations = annotations.prepend(anno); } - injectMethod(builderType, newMethod); + injectMethod(job.builderType, newMethod); } private void addObtainVia(BuilderFieldData bfd, JavacNode node) { @@ -1032,17 +1079,17 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { return null; } - private ListBuffer<JCExpression> getTypeParamExpressions(List<? extends JCTree> typeParams, JavacTreeMaker maker, Context context) { + private ListBuffer<JCExpression> getTypeParamExpressions(List<? extends JCTree> typeParams, JavacTreeMaker maker, JavacNode source) { ListBuffer<JCExpression> typeParamsForBuilderParameter = new ListBuffer<JCExpression>(); for (JCTree typeParam : typeParams) { if (typeParam instanceof JCTypeParameter) { - typeParamsForBuilderParameter.add(maker.Ident(((JCTypeParameter)typeParam).getName())); + typeParamsForBuilderParameter.append(maker.Ident(((JCTypeParameter)typeParam).getName())); } else if (typeParam instanceof JCIdent) { - typeParamsForBuilderParameter.add(maker.Ident(((JCIdent)typeParam).getName())); + typeParamsForBuilderParameter.append(maker.Ident(((JCIdent)typeParam).getName())); } else if (typeParam instanceof JCFieldAccess) { - typeParamsForBuilderParameter.add(copySelect(maker, (JCFieldAccess) typeParam)); + typeParamsForBuilderParameter.append(copySelect(maker, (JCFieldAccess) typeParam)); } else if (typeParam instanceof JCTypeApply) { - typeParamsForBuilderParameter.add(cloneType(maker, (JCTypeApply)typeParam, typeParam, context)); + typeParamsForBuilderParameter.append(cloneType(maker, (JCTypeApply)typeParam, source)); } } return typeParamsForBuilderParameter; diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilderRemove.java b/src/core/lombok/javac/handlers/HandleSuperBuilderRemove.java index cca69729..d3db5ef4 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilderRemove.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilderRemove.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Project Lombok Authors. + * Copyright (C) 2020-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,8 +23,6 @@ package lombok.javac.handlers; import static lombok.javac.handlers.JavacHandlerUtil.*; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.tree.JCTree.JCAnnotation; import lombok.core.AlreadyHandledAnnotations; @@ -33,9 +31,10 @@ import lombok.core.HandlerPriority; import lombok.experimental.SuperBuilder; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; +import lombok.spi.Provides; -@ProviderFor(JavacAnnotationHandler.class) -@HandlerPriority(65536) +@Provides +@HandlerPriority(32768) @AlreadyHandledAnnotations public class HandleSuperBuilderRemove extends JavacAnnotationHandler<SuperBuilder> { @Override public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast, JavacNode annotationNode) { diff --git a/src/core/lombok/javac/handlers/HandleSynchronized.java b/src/core/lombok/javac/handlers/HandleSynchronized.java index 85dc7e80..0bf7cbe8 100644 --- a/src/core/lombok/javac/handlers/HandleSynchronized.java +++ b/src/core/lombok/javac/handlers/HandleSynchronized.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,8 +33,7 @@ import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; @@ -46,13 +45,12 @@ import com.sun.tools.javac.tree.JCTree.JCNewArray; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; -import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; /** * Handles the {@code lombok.Synchronized} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleSynchronized extends JavacAnnotationHandler<Synchronized> { private static final String INSTANCE_LOCK_NAME = "$lock"; @@ -93,7 +91,6 @@ public class HandleSynchronized extends JavacAnnotationHandler<Synchronized> { } JavacTreeMaker maker = methodNode.getTreeMaker().at(ast.pos); - Context context = methodNode.getContext(); MemberExistsResult exists = MemberExistsResult.NOT_EXISTS; @@ -124,7 +121,7 @@ public class HandleSynchronized extends JavacAnnotationHandler<Synchronized> { List.<JCExpression>of(maker.Literal(CTC_INT, 0)), null); JCVariableDecl fieldDecl = recursiveSetGeneratedBy(maker.VarDef( maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (isStatic[0] ? Flags.STATIC : 0)), - methodNode.toName(lockName), objectType, newObjectArray), ast, context); + methodNode.toName(lockName), objectType, newObjectArray), annotationNode); injectFieldAndMarkGenerated(methodNode.up(), fieldDecl); } @@ -137,8 +134,8 @@ public class HandleSynchronized extends JavacAnnotationHandler<Synchronized> { lockNode = maker.Select(maker.Ident(methodNode.toName("this")), methodNode.toName(lockName)); } - recursiveSetGeneratedBy(lockNode, ast, context); - method.body = setGeneratedBy(maker.Block(0, List.<JCStatement>of(setGeneratedBy(maker.Synchronized(lockNode, method.body), ast, context))), ast, context); + recursiveSetGeneratedBy(lockNode, annotationNode); + method.body = setGeneratedBy(maker.Block(0, List.<JCStatement>of(setGeneratedBy(maker.Synchronized(lockNode, method.body), annotationNode))), annotationNode); methodNode.rebuild(); } diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index ff0016e4..3fc6a4e4 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,11 +38,9 @@ import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; import com.sun.tools.javac.tree.JCTree.JCBlock; @@ -60,7 +58,7 @@ import com.sun.tools.javac.util.List; /** * Handles the {@code ToString} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleToString extends JavacAnnotationHandler<ToString> { @Override public void handle(AnnotationValues<ToString> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString"); @@ -135,7 +133,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { } } } - JCMethodDecl method = createToString(typeNode, members, includeFieldNames, callSuper, fieldAccess, source.get()); + JCMethodDecl method = createToString(typeNode, members, includeFieldNames, callSuper, fieldAccess, source); injectMethod(typeNode, method); break; case EXISTS_BY_LOMBOK: @@ -150,7 +148,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { } static JCMethodDecl createToString(JavacNode typeNode, Collection<Included<JavacNode, ToString.Include>> members, - boolean includeNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) { + boolean includeNames, boolean callSuper, FieldAccess fieldAccess, JavacNode source) { JavacTreeMaker maker = typeNode.getTreeMaker(); @@ -250,7 +248,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { JCMethodDecl methodDef = maker.MethodDef(mods, typeNode.toName("toString"), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); createRelevantNonNullAnnotation(typeNode, methodDef); - return recursiveSetGeneratedBy(methodDef, source, typeNode.getContext()); + return recursiveSetGeneratedBy(methodDef, source); } public static String getTypeName(JavacNode typeNode) { diff --git a/src/core/lombok/javac/handlers/HandleUtilityClass.java b/src/core/lombok/javac/handlers/HandleUtilityClass.java index 9ff33684..e006cc47 100644 --- a/src/core/lombok/javac/handlers/HandleUtilityClass.java +++ b/src/core/lombok/javac/handlers/HandleUtilityClass.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,8 +25,6 @@ import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; import static lombok.javac.Javac.CTC_VOID; import static lombok.javac.handlers.JavacHandlerUtil.*; -import org.mangosdk.spi.ProviderFor; - import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree.JCAnnotation; @@ -50,12 +48,13 @@ import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; +import lombok.spi.Provides; /** * Handles the {@code @UtilityClass} annotation for javac. */ @HandlerPriority(-4096) //-2^12; to ensure @FieldDefaults picks up on the 'static' we set here. -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> { @Override public void handle(AnnotationValues<UtilityClass> annotation, JCAnnotation ast, JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.UTILITY_CLASS_FLAG_USAGE, "@UtilityClass"); @@ -69,7 +68,7 @@ public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> { private static boolean checkLegality(JavacNode typeNode, JavacNode errorNode) { if (!isClass(typeNode)) { - errorNode.addError("@UtilityClass is only supported on a class (can't be an interface, enum, annotation, or record)."); + errorNode.addError("@UtilityClass is only supported on a class."); return false; } @@ -141,7 +140,7 @@ public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> { Name name = typeNode.toName("<init>"); JCBlock block = maker.Block(0L, createThrowStatement(typeNode, maker)); JCMethodDecl methodDef = maker.MethodDef(mods, name, null, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), block, null); - JCMethodDecl constructor = recursiveSetGeneratedBy(methodDef, typeNode.get(), typeNode.getContext()); + JCMethodDecl constructor = recursiveSetGeneratedBy(methodDef, typeNode); JavacHandlerUtil.injectMethod(typeNode, constructor, List.<Type>nil(), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); } diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 23c27bc0..7a524ca3 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2018 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,8 +33,7 @@ import lombok.javac.JavacASTVisitor; import lombok.javac.JavacNode; import lombok.javac.JavacResolution; import lombok.javac.ResolutionResetNeeded; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symtab; @@ -49,7 +48,7 @@ import com.sun.tools.javac.tree.JCTree.JCNewArray; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; -@ProviderFor(JavacASTVisitor.class) +@Provides(JavacASTVisitor.class) @HandlerPriority(HANDLE_DELEGATE_PRIORITY + 100) // run slightly after HandleDelegate; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated. @ResolutionResetNeeded public class HandleVal extends JavacASTAdapter { @@ -63,6 +62,7 @@ public class HandleVal extends JavacASTAdapter { JCTree typeTree = local.vartype; if (typeTree == null) return; String typeTreeToString = typeTree.toString(); + JavacNode typeNode = localNode.getNodeFor(typeTree); if (!(eq(typeTreeToString, "val") || eq(typeTreeToString, "var"))) return; boolean isVal = typeMatches(val.class, localNode, typeTree); @@ -111,7 +111,7 @@ public class HandleVal extends JavacASTAdapter { if (isVal) local.mods.flags |= Flags.FINAL; if (!localNode.shouldDeleteLombokAnnotations()) { - JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), typeTree, localNode.getContext()); + JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), typeNode); local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation); } @@ -182,7 +182,7 @@ public class HandleVal extends JavacASTAdapter { local.vartype = JavacResolution.createJavaLangObject(localNode.getAst()); throw e; } finally { - recursiveSetGeneratedBy(local.vartype, typeTree, localNode.getContext()); + recursiveSetGeneratedBy(local.vartype, typeNode); } } } diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java index abc5a5ca..cff29801 100644 --- a/src/core/lombok/javac/handlers/HandleValue.java +++ b/src/core/lombok/javac/handlers/HandleValue.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2018 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,8 +33,7 @@ import lombok.Value; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree.JCAnnotation; @@ -45,7 +44,7 @@ import com.sun.tools.javac.util.List; /** * Handles the {@code lombok.Value} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides @HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier. public class HandleValue extends JavacAnnotationHandler<Value> { private HandleFieldDefaults handleFieldDefaults = new HandleFieldDefaults(); @@ -72,7 +71,6 @@ public class HandleValue extends JavacAnnotationHandler<Value> { JCModifiers jcm = ((JCClassDecl) typeNode.get()).mods; if ((jcm.flags & Flags.FINAL) == 0) { jcm.flags |= Flags.FINAL; - typeNode.rebuild(); } } handleFieldDefaults.generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true); diff --git a/src/core/lombok/javac/handlers/HandleWith.java b/src/core/lombok/javac/handlers/HandleWith.java index 4e35a574..f914b4c7 100644 --- a/src/core/lombok/javac/handlers/HandleWith.java +++ b/src/core/lombok/javac/handlers/HandleWith.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2020 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,8 +37,7 @@ import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Type; @@ -62,7 +61,7 @@ import com.sun.tools.javac.util.Name; /** * Handles the {@code lombok.With} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleWith extends JavacAnnotationHandler<With> { public void generateWithForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelWith) { if (checkForTypeLevelWith) { @@ -215,6 +214,7 @@ public class HandleWith extends JavacAnnotationHandler<With> { ClassSymbol sym = ((JCClassDecl) fieldNode.up().get()).sym; Type returnType = sym == null ? null : sym.type; + recursiveSetGeneratedBy(createdWith, source); injectMethod(typeNode, createdWith, List.<Type>of(getMirrorForFieldType(fieldNode)), returnType); } @@ -234,7 +234,7 @@ public class HandleWith extends JavacAnnotationHandler<With> { long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, field.getContext()); List<JCAnnotation> annsOnParam = copyAnnotations(onParam).appendList(copyableAnnotations); - JCExpression pType = cloneType(maker, fieldDecl.vartype, source.get(), source.getContext()); + JCExpression pType = cloneType(maker, fieldDecl.vartype, source); JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, annsOnParam), fieldDecl.name, pType, null); if (!makeAbstract) { @@ -289,7 +289,7 @@ public class HandleWith extends JavacAnnotationHandler<With> { if (makeAbstract) access = access | Flags.ABSTRACT; JCMethodDecl decl = recursiveSetGeneratedBy(maker.MethodDef(maker.Modifiers(access, annsOnMethod), methodName, returnType, - methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source.get(), field.getContext()); + methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source); copyJavadoc(field, decl, CopyJavadoc.WITH); return decl; } diff --git a/src/core/lombok/javac/handlers/HandleWithBy.java b/src/core/lombok/javac/handlers/HandleWithBy.java index 19673172..f1f953b3 100644 --- a/src/core/lombok/javac/handlers/HandleWithBy.java +++ b/src/core/lombok/javac/handlers/HandleWithBy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Project Lombok Authors. + * Copyright (C) 2020-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,8 +41,7 @@ import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.JavacTreeMaker.TypeTag; import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; @@ -67,7 +66,7 @@ import com.sun.tools.javac.util.Name; /** * Handles the {@code lombok.With} annotation for javac. */ -@ProviderFor(JavacAnnotationHandler.class) +@Provides public class HandleWithBy extends JavacAnnotationHandler<WithBy> { public void generateWithByForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelWithBy) { if (checkForTypeLevelWithBy) { @@ -126,7 +125,6 @@ public class HandleWithBy extends JavacAnnotationHandler<WithBy> { @Override public void handle(AnnotationValues<WithBy> annotation, JCAnnotation ast, JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.WITHBY_FLAG_USAGE, "@WithBy"); - Collection<JavacNode> fields = annotationNode.upFromAnnotationToFields(); deleteAnnotationIfNeccessary(annotationNode, WithBy.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode node = annotationNode.up(); @@ -138,7 +136,7 @@ public class HandleWithBy extends JavacAnnotationHandler<WithBy> { switch (node.getKind()) { case FIELD: - createWithByForFields(level, fields, annotationNode, true, onMethod); + createWithByForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, true, onMethod); break; case TYPE: if (!onMethod.isEmpty()) annotationNode.addError("'onMethod' is not supported for @WithBy on a type."); @@ -209,6 +207,7 @@ public class HandleWithBy extends JavacAnnotationHandler<WithBy> { ClassSymbol sym = ((JCClassDecl) fieldNode.up().get()).sym; Type returnType = sym == null ? null : sym.type; + recursiveSetGeneratedBy(createdWithBy, source); injectMethod(typeNode, createdWithBy, List.<Type>of(getMirrorForFieldType(fieldNode)), returnType); } @@ -265,7 +264,7 @@ public class HandleWithBy extends JavacAnnotationHandler<WithBy> { } if (functionalInterfaceName == null) { functionalInterfaceName = NAME_JUF_FUNCTION; - parameterizer = cloneType(maker, fieldDecl.vartype, source.get(), field.getContext()); + parameterizer = cloneType(maker, fieldDecl.vartype, source); } if (functionalInterfaceName == NAME_JUF_INTOP) applyMethodName = "applyAsInt"; if (functionalInterfaceName == NAME_JUF_LONGOP) applyMethodName = "applyAsLong"; @@ -274,7 +273,7 @@ public class HandleWithBy extends JavacAnnotationHandler<WithBy> { JCExpression varType = chainDots(field, functionalInterfaceName); if (parameterizer != null && superExtendsStyle) { JCExpression parameterizer1 = parameterizer; - JCExpression parameterizer2 = cloneType(maker, parameterizer, source.get(), field.getContext()); + JCExpression parameterizer2 = cloneType(maker, parameterizer, source); // TODO: Apply copyable annotations to 'parameterizer' and 'parameterizer2'. JCExpression arg1 = maker.Wildcard(maker.TypeBoundKind(BoundKind.SUPER), parameterizer1); JCExpression arg2 = maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), parameterizer2); @@ -334,7 +333,7 @@ public class HandleWithBy extends JavacAnnotationHandler<WithBy> { if (makeAbstract) access = access | Flags.ABSTRACT; createRelevantNonNullAnnotation(source, param); JCMethodDecl decl = recursiveSetGeneratedBy(maker.MethodDef(maker.Modifiers(access, annsOnMethod), methodName, returnType, - methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source.get(), field.getContext()); + methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source); copyJavadoc(field, decl, CopyJavadoc.WITH_BY); createRelevantNonNullAnnotation(source, decl); return decl; diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index cd4bc70a..b4f2acc4 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,7 +35,6 @@ 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; @@ -103,7 +102,6 @@ import lombok.core.handlers.HandlerUtil.FieldAccess; import lombok.delombok.LombokOptionsFactory; import lombok.experimental.Accessors; import lombok.experimental.Tolerate; -import lombok.javac.Java14Flags; import lombok.javac.Javac; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -118,17 +116,15 @@ public class JavacHandlerUtil { } private static class MarkingScanner extends TreeScanner { - private final JCTree source; - private final Context context; + private final JavacNode source; - MarkingScanner(JCTree source, Context context) { + MarkingScanner(JavacNode source) { this.source = source; - this.context = context; } @Override public void scan(JCTree tree) { if (tree == null) return; - setGeneratedBy(tree, source, context); + setGeneratedBy(tree, source); super.scan(tree); } } @@ -146,12 +142,12 @@ public class JavacHandlerUtil { Options options = Options.instance(context); return (options.keySet().contains("ide") && !options.keySet().contains("backgroundCompilation")); } - + public static boolean inNetbeansCompileOnSave(Context context) { Options options = Options.instance(context); return (options.keySet().contains("ide") && options.keySet().contains("backgroundCompilation")); } - + public static JCTree getGeneratedBy(JCTree node) { return JCTree_generatedNode.get(node); } @@ -160,20 +156,31 @@ public class JavacHandlerUtil { return getGeneratedBy(node) != null; } - public static <T extends JCTree> T recursiveSetGeneratedBy(T node, JCTree source, Context context) { + public static <T extends JCTree> T recursiveSetGeneratedBy(T node, JavacNode source) { if (node == null) return null; - setGeneratedBy(node, source, context); - node.accept(new MarkingScanner(source, context)); + setGeneratedBy(node, source); + node.accept(new MarkingScanner(source)); return node; } - public static <T extends JCTree> T setGeneratedBy(T node, JCTree source, Context context) { + public static <T extends JCTree> T setGeneratedBy(T node, JavacNode sourceNode) { if (node == null) return null; - if (source == null) JCTree_generatedNode.clear(node); - else JCTree_generatedNode.set(node, source); - if (source != null && (!inNetbeansEditor(context) || (node instanceof JCVariableDecl && (((JCVariableDecl) node).mods.flags & Flags.PARAMETER) != 0))) node.pos = source.pos; + if (sourceNode == null) { + JCTree_generatedNode.clear(node); + return node; + } + JCTree_generatedNode.set(node, sourceNode.get()); + + if (!inNetbeansEditor(sourceNode.getContext()) || isParameter(node)) { + node.pos = sourceNode.getStartPos(); + storeEnd(node, sourceNode.getEndPosition(), (JCCompilationUnit) sourceNode.top().get()); + } return node; } + + public static boolean isParameter(JCTree node) { + return node instanceof JCVariableDecl && (((JCVariableDecl) node).mods.flags & Flags.PARAMETER) != 0; + } public static boolean hasAnnotation(String type, JavacNode node) { return hasAnnotation(type, node, false); @@ -800,7 +807,7 @@ public class JavacHandlerUtil { node = upToTypeNode(node); if (node != null && node.get() instanceof JCClassDecl) { - for (JCTree def : ((JCClassDecl)node.get()).defs) { + for (JCTree def : ((JCClassDecl) node.get()).defs) { if (def instanceof JCMethodDecl) { JCMethodDecl md = (JCMethodDecl) def; if (md.name.contentEquals("<init>")) { @@ -816,23 +823,21 @@ public class JavacHandlerUtil { } /** - * Checks if there is a constructor generated by lombok. + * Checks if there is at least one constructor that is generated by lombok. * - * @param node Any node that represents the Type (JCClassDecl) to look in, or any child node thereof. + * @param node Any node that represents the Type (TypeDeclaration) to look in, or any child node thereof. */ public static boolean lombokConstructorExists(JavacNode node) { node = upToTypeNode(node); if (node != null && node.get() instanceof JCClassDecl) { - for (JCTree def : ((JCClassDecl)node.get()).defs) { + for (JCTree def : ((JCClassDecl) node.get()).defs) { if (def instanceof JCMethodDecl) { JCMethodDecl md = (JCMethodDecl) def; if (md.name.contentEquals("<init>")) { if ((md.mods.flags & Flags.GENERATEDCONSTR) != 0) continue; if (isTolerate(node, md)) continue; - if (getGeneratedBy(def) != null) { - return true; - } + if (getGeneratedBy(def) != null) return true; } } } @@ -1054,7 +1059,7 @@ public class JavacHandlerUtil { public static JavacNode injectField(JavacNode typeNode, JCVariableDecl field) { return injectField(typeNode, field, false); } - + public static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addGenerated) { return injectField(typeNode, field, addGenerated, false); } @@ -1063,8 +1068,8 @@ public class JavacHandlerUtil { JCClassDecl type = (JCClassDecl) typeNode.get(); if (addGenerated) { - addSuppressWarningsAll(field.mods, typeNode, field.pos, getGeneratedBy(field), typeNode.getContext()); - addGenerated(field.mods, typeNode, field.pos, getGeneratedBy(field), typeNode.getContext()); + addSuppressWarningsAll(field.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(field)), typeNode.getContext()); + addGenerated(field.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(field)), typeNode.getContext()); } List<JCTree> insertAfter = null; @@ -1104,6 +1109,17 @@ public class JavacHandlerUtil { private static Constructor<?> CONSTRUCTOR; private static Field ANNOTATIONS, UNDERLYING_TYPE; + private static void initByLoader(ClassLoader classLoader) { + if (TYPE != null) return; + Class<?> c; + try { + c = classLoader.loadClass("com.sun.tools.javac.tree.JCTree$JCAnnotatedType"); + } catch (Exception e) { + return; + } + init(c); + } + private static void init(Class<?> in) { if (TYPE != null) return; if (!in.getName().equals("com.sun.tools.javac.tree.JCTree$JCAnnotatedType")) return; @@ -1148,17 +1164,18 @@ public class JavacHandlerUtil { } static JCExpression create(List<JCAnnotation> annotations, JCExpression underlyingType) { + initByLoader(underlyingType.getClass().getClassLoader()); try { return (JCExpression) CONSTRUCTOR.newInstance(annotations, underlyingType); } catch (Exception e) { - return null; + return underlyingType; } } } - + static class JCAnnotationReflect { private static Field ATTRIBUTE; - + static { try { ATTRIBUTE = Permit.getField(JCAnnotation.class, "attribute"); @@ -1199,7 +1216,7 @@ public class JavacHandlerUtil { try { Scope scope = (Scope) membersField.get(from); if (scope == null) return; - removeMethod.invoke(scope, toRemove); + Permit.invoke(removeMethod, scope, toRemove); } catch (Exception e) {} } @@ -1208,7 +1225,7 @@ public class JavacHandlerUtil { try { Scope scope = (Scope) membersField.get(from); if (scope == null) return; - enterMethod.invoke(scope, toEnter); + Permit.invoke(enterMethod, scope, toEnter); } catch (Exception e) {} } } @@ -1227,7 +1244,7 @@ public class JavacHandlerUtil { 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; @@ -1244,11 +1261,11 @@ public class JavacHandlerUtil { idx++; } } - - addSuppressWarningsAll(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext()); - addGenerated(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext()); + + addSuppressWarningsAll(method.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(method)), typeNode.getContext()); + addGenerated(method.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(method)), typeNode.getContext()); type.defs = type.defs.append(method); - + List<Symbol.VarSymbol> params = null; if (method.getParameters() != null && !method.getParameters().isEmpty()) { ListBuffer<Symbol.VarSymbol> newParams = new ListBuffer<Symbol.VarSymbol>(); @@ -1257,6 +1274,7 @@ public class JavacHandlerUtil { 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); + varSymbol.adr = 1 << i; List<JCAnnotation> annotations = param.getModifiers().getAnnotations(); if (annotations != null && !annotations.isEmpty()) { ListBuffer<Attribute.Compound> newAnnotations = new ListBuffer<Attribute.Compound>(); @@ -1278,9 +1296,9 @@ public class JavacHandlerUtil { 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); } @@ -1290,6 +1308,9 @@ public class JavacHandlerUtil { MethodSymbol methodSymbol = new MethodSymbol(access, methodName, new MethodType(paramTypes, returnType, List.<Type>nil(), Symtab.instance(context).methodClass), cs); if (params != null && !params.isEmpty()) { methodSymbol.params = params; + for (VarSymbol varSymbol : params) { + varSymbol.owner = methodSymbol; + } } ClassSymbolMembersField.enter(cs, methodSymbol); } @@ -1303,8 +1324,8 @@ public class JavacHandlerUtil { */ public static JavacNode injectType(JavacNode typeNode, final JCClassDecl type) { JCClassDecl typeDecl = (JCClassDecl) typeNode.get(); - addSuppressWarningsAll(type.mods, typeNode, type.pos, getGeneratedBy(type), typeNode.getContext()); - addGenerated(type.mods, typeNode, type.pos, getGeneratedBy(type), typeNode.getContext()); + addSuppressWarningsAll(type.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(type)), typeNode.getContext()); + addGenerated(type.mods, typeNode, typeNode.getNodeFor(getGeneratedBy(type)), typeNode.getContext()); typeDecl.defs = typeDecl.defs.append(type); return typeNode.add(type, Kind.TYPE); } @@ -1343,7 +1364,7 @@ public class JavacHandlerUtil { } } - public static void addSuppressWarningsAll(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context) { + public static void addSuppressWarningsAll(JCModifiers mods, JavacNode node, JavacNode source, Context context) { if (!LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateSuppressWarnings()) return; boolean addJLSuppress = !Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_SUPPRESSWARNINGS_ANNOTATIONS)); @@ -1359,27 +1380,27 @@ public class JavacHandlerUtil { } } } - if (addJLSuppress) addAnnotation(mods, node, pos, source, context, "java.lang.SuppressWarnings", node.getTreeMaker().Literal("all")); + if (addJLSuppress) addAnnotation(mods, node, source, "java.lang.SuppressWarnings", node.getTreeMaker().Literal("all")); if (Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_FINDBUGS_SUPPRESSWARNINGS_ANNOTATIONS))) { JavacTreeMaker maker = node.getTreeMaker(); JCExpression arg = maker.Assign(maker.Ident(node.toName("justification")), maker.Literal("generated code")); - addAnnotation(mods, node, pos, source, context, "edu.umd.cs.findbugs.annotations.SuppressFBWarnings", arg); + addAnnotation(mods, node, source, "edu.umd.cs.findbugs.annotations.SuppressFBWarnings", arg); } } - public static void addGenerated(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context) { + public static void addGenerated(JCModifiers mods, JavacNode node, JavacNode source, Context context) { if (!LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateGenerated()) return; if (HandlerUtil.shouldAddGenerated(node)) { - addAnnotation(mods, node, pos, source, context, "javax.annotation.Generated", node.getTreeMaker().Literal("lombok")); + addAnnotation(mods, node, source, "javax.annotation.Generated", node.getTreeMaker().Literal("lombok")); } if (Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_LOMBOK_GENERATED_ANNOTATIONS))) { - addAnnotation(mods, node, pos, source, context, "lombok.Generated", null); + addAnnotation(mods, node, source, "lombok.Generated", null); } } - public static void addAnnotation(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context, String annotationTypeFqn, JCExpression arg) { + public static void addAnnotation(JCModifiers mods, JavacNode node, JavacNode source, String annotationTypeFqn, JCExpression arg) { boolean isJavaLangBased; String simpleName; { int idx = annotationTypeFqn.lastIndexOf('.'); @@ -1402,17 +1423,8 @@ public class JavacHandlerUtil { JavacTreeMaker maker = node.getTreeMaker(); JCExpression annType = isJavaLangBased ? genJavaLangTypeRef(node, simpleName) : chainDotsString(node, annotationTypeFqn); - annType.pos = pos; - if (arg != null) { - arg.pos = pos; - if (arg instanceof JCAssign) { - ((JCAssign) arg).lhs.pos = pos; - ((JCAssign) arg).rhs.pos = pos; - } - } List<JCExpression> argList = arg != null ? List.of(arg) : List.<JCExpression>nil(); - JCAnnotation annotation = recursiveSetGeneratedBy(maker.Annotation(annType, argList), source, context); - annotation.pos = pos; + JCAnnotation annotation = recursiveSetGeneratedBy(maker.Annotation(annType, argList), source); mods.annotations = mods.annotations.append(annotation); } @@ -1456,6 +1468,7 @@ public class JavacHandlerUtil { } return e; } + /** * In javac, dotted access of any kind, from {@code java.lang.String} to {@code var.methodName} * is represented by a fold-left of {@code Select} nodes with the leftmost string represented by @@ -1831,13 +1844,12 @@ public class JavacHandlerUtil { if (params == null || params.isEmpty()) return params; ListBuffer<JCTypeParameter> out = new ListBuffer<JCTypeParameter>(); JavacTreeMaker maker = source.getTreeMaker(); - Context context = source.getContext(); for (JCTypeParameter tp : params) { List<JCExpression> bounds = tp.bounds; if (bounds != null && !bounds.isEmpty()) { ListBuffer<JCExpression> boundsCopy = new ListBuffer<JCExpression>(); for (JCExpression expr : tp.bounds) { - boundsCopy.append(cloneType(maker, expr, source.get(), context)); + boundsCopy.append(cloneType(maker, expr, source)); } bounds = boundsCopy.toList(); } @@ -1859,20 +1871,30 @@ public class JavacHandlerUtil { public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, JavacNode type, List<JCTypeParameter> params) { JCClassDecl td = (JCClassDecl) type.get(); boolean instance = (td.mods.flags & Flags.STATIC) == 0; - return namePlusTypeParamsToTypeReference(maker, type.up(), td.name, instance, params); + return namePlusTypeParamsToTypeReference(maker, type.up(), td.name, instance, params, List.<JCAnnotation>nil()); + } + + public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, JavacNode type, List<JCTypeParameter> params, List<JCAnnotation> annotations) { + JCClassDecl td = (JCClassDecl) type.get(); + boolean instance = (td.mods.flags & Flags.STATIC) == 0; + return namePlusTypeParamsToTypeReference(maker, type.up(), td.name, instance, params, annotations); } public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, JavacNode parentType, Name typeName, boolean instance, List<JCTypeParameter> params) { + return namePlusTypeParamsToTypeReference(maker, parentType, typeName, instance, params, List.<JCAnnotation>nil()); + } + + public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, JavacNode parentType, Name typeName, boolean instance, List<JCTypeParameter> params, List<JCAnnotation> annotations) { JCExpression r = null; - if (parentType != null && parentType.getKind() == Kind.TYPE) { JCClassDecl td = (JCClassDecl) parentType.get(); boolean outerInstance = instance && ((td.mods.flags & Flags.STATIC) == 0); List<JCTypeParameter> outerParams = instance ? td.typarams : List.<JCTypeParameter>nil(); - r = namePlusTypeParamsToTypeReference(maker, parentType.up(), td.name, outerInstance, outerParams); + r = namePlusTypeParamsToTypeReference(maker, parentType.up(), td.name, outerInstance, outerParams, List.<JCAnnotation>nil()); } r = r == null ? maker.Ident(typeName) : maker.Select(r, typeName); + if (!annotations.isEmpty()) r = JCAnnotatedTypeReflect.create(annotations, r); if (!params.isEmpty()) r = maker.TypeApply(r, typeParameterNames(maker, params)); return r; } @@ -1926,17 +1948,30 @@ public class JavacHandlerUtil { return out.toList(); } - static boolean isClass(JavacNode typeNode) { - return isClassAndDoesNotHaveFlags(typeNode, Flags.INTERFACE | Flags.ENUM | Flags.ANNOTATION | Java14Flags.RECORD); + /** + * Returns {@code true} if the provided node is an actual class and not some other type declaration (so, not an annotation definition, interface, enum, or record). + */ + public static boolean isClass(JavacNode typeNode) { + return isClassAndDoesNotHaveFlags(typeNode, Flags.INTERFACE | Flags.ENUM | Flags.ANNOTATION | RECORD); + } + + /** + * Returns {@code true} if the provided node is an actual class or enum and not some other type declaration (so, not an annotation definition, interface, or record). + */ + public static boolean isClassOrEnum(JavacNode typeNode) { + return isClassAndDoesNotHaveFlags(typeNode, Flags.INTERFACE | Flags.ANNOTATION | RECORD); } - static boolean isClassOrEnum(JavacNode typeNode) { - return isClassAndDoesNotHaveFlags(typeNode, Flags.INTERFACE | Flags.ANNOTATION | Java14Flags.RECORD); + /** + * Returns {@code true} if the provided node is a record declaration (so, not an annotation definition, interface, enum, or plain class). + */ + public static boolean isRecord(JavacNode typeNode) { + return typeNode.getKind() == Kind.TYPE && (((JCClassDecl) typeNode.get()).mods.flags & RECORD) != 0; } public static boolean isClassAndDoesNotHaveFlags(JavacNode typeNode, long flags) { JCClassDecl typeDecl = null; - if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl)typeNode.get(); + if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); else return false; long typeDeclflags = typeDecl == null ? 0 : typeDecl.mods.flags; @@ -1950,11 +1985,11 @@ public class JavacHandlerUtil { return node; } - public static List<JCExpression> cloneTypes(JavacTreeMaker maker, List<JCExpression> in, JCTree source, Context context) { + public static List<JCExpression> cloneTypes(JavacTreeMaker maker, List<JCExpression> in, JavacNode source) { if (in.isEmpty()) return List.nil(); - if (in.size() == 1) return List.of(cloneType(maker, in.get(0), source, context)); + if (in.size() == 1) return List.of(cloneType(maker, in.get(0), source)); ListBuffer<JCExpression> lb = new ListBuffer<JCExpression>(); - for (JCExpression expr : in) lb.append(cloneType(maker, expr, source, context)); + for (JCExpression expr : in) lb.append(cloneType(maker, expr, source)); return lb.toList(); } @@ -1968,9 +2003,9 @@ public class JavacHandlerUtil { * the class's own parameter, but as its a static method, the static method's notion of {@code T} is different from the class notion of {@code T}. If you're duplicating * a type used in the class context, you need to use this method. */ - public static JCExpression cloneType(JavacTreeMaker maker, JCExpression in, JCTree source, Context context) { + public static JCExpression cloneType(JavacTreeMaker maker, JCExpression in, JavacNode source) { JCExpression out = cloneType0(maker, in); - if (out != null) recursiveSetGeneratedBy(out, source, context); + if (out != null) recursiveSetGeneratedBy(out, source); return out; } @@ -2031,50 +2066,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); - 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; - } - public static enum CopyJavadoc { VERBATIM { @Override public String apply(final JCCompilationUnit cu, final JavacNode node) { @@ -2089,7 +2080,7 @@ public class JavacHandlerUtil { String out = getJavadocSection(javadoc, "GETTER"); final boolean sectionBased = out != null; if (!sectionBased) { - out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@param(?:eter)?\\s+.*"); + out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), JavadocTag.PARAM); } node.getAst().cleanupTask("javadocfilter-getter", n, new CleanupTask() { @Override public void cleanup() { @@ -2097,7 +2088,7 @@ public class JavacHandlerUtil { if (javadoc == null || javadoc.isEmpty()) return; javadoc = stripSectionsFromJavadoc(javadoc); if (!sectionBased) { - javadoc = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@returns?\\s+.*"); + javadoc = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), JavadocTag.RETURN); } Javac.setDocComment(cu, n, javadoc); } @@ -2130,7 +2121,7 @@ public class JavacHandlerUtil { String out = getJavadocSection(javadoc, sectionName); final boolean sectionBased = out != null; if (!sectionBased) { - out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@returns?\\s+.*"); + out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), JavadocTag.RETURN); } node.getAst().cleanupTask("javadocfilter-setter", n, new CleanupTask() { @Override public void cleanup() { @@ -2138,7 +2129,7 @@ public class JavacHandlerUtil { if (javadoc == null || javadoc.isEmpty()) return; javadoc = stripSectionsFromJavadoc(javadoc); if (!sectionBased) { - javadoc = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@param(?:eter)?\\s+.*"); + javadoc = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), JavadocTag.PARAM); } Javac.setDocComment(cu, n, javadoc); } @@ -2150,6 +2141,7 @@ public class JavacHandlerUtil { public static void copyJavadoc(JavacNode from, JCTree to, CopyJavadoc copyMode) { copyJavadoc(from, to, copyMode, false); } + /** * Copies javadoc on one node to the other. * @@ -2164,31 +2156,13 @@ public class JavacHandlerUtil { try { JCCompilationUnit cu = ((JCCompilationUnit) from.top().get()); String newJavadoc = copyMode.apply(cu, from); - if (newJavadoc != null) { - if (forceAddReturn) newJavadoc = addReturnsThisIfNeeded(newJavadoc); - Javac.setDocComment(cu, to, newJavadoc); + if (forceAddReturn) { + newJavadoc = addReturnsThisIfNeeded(newJavadoc); } + Javac.setDocComment(cu, to, newJavadoc); } 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()); @@ -2232,6 +2206,10 @@ public class JavacHandlerUtil { JCExpression resType = mth.restype; if (resType instanceof JCTypeApply) { JCTypeApply ta = (JCTypeApply) resType; + if (ta.clazz instanceof JCFieldAccess) { + mth.restype = maker.TypeApply(maker.AnnotatedType(List.of(m), ta.clazz), ta.arguments); + return; + } resType = ta.clazz; } @@ -2287,8 +2265,4 @@ public class JavacHandlerUtil { arg.mods.annotations = arg.mods.annotations == null ? List.of(m) : arg.mods.annotations.prepend(m); } } - - public static boolean isRecord(JavacNode typeNode) { - return typeNode.getKind() == Kind.TYPE && (((JCClassDecl) typeNode.get()).mods.flags & Java14Flags.RECORD) != 0; - } } diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 7cd52c8c..4ca09b82 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -43,7 +43,6 @@ import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWildcard; -import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; @@ -57,6 +56,7 @@ import lombok.core.configuration.CheckerFrameworkVersion; import lombok.core.handlers.HandlerUtil; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; +import lombok.javac.handlers.HandleBuilder.BuilderJob; public class JavacSingularsRecipes { public interface ExpressionMaker { @@ -235,7 +235,7 @@ public class JavacSingularsRecipes { return Arrays.asList(p, s); } - public abstract java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source); + public abstract java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JavacNode source); /** * Generates the singular, plural, and clear methods for the given {@link SingularData}. @@ -243,36 +243,38 @@ public class JavacSingularsRecipes { * If you need more control over the return type and value, use * {@link #generateMethods(SingularData, boolean, JavacNode, JCTree, boolean, ExpressionMaker, StatementMaker)}. */ - public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, final JavacNode builderType, JCTree source, boolean fluent, final boolean chain, AccessLevel access) { - final JavacTreeMaker maker = builderType.getTreeMaker(); + public void generateMethods(final BuilderJob job, SingularData data, boolean deprecate) { + //job.checkerFramework, job.builderType, job.source, job.oldFluent, job.oldChain, job.accessInners + //CheckerFrameworkVersion cfv, final JavacNode builderType, JavacNode source, boolean fluent, final boolean chain, AccessLevel access) { + final JavacTreeMaker maker = job.builderType.getTreeMaker(); ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() { - return chain ? - cloneSelfType(builderType) : - maker.Type(createVoidType(builderType.getSymbolTable(), CTC_VOID)); + return job.oldChain ? + cloneSelfType(job.builderType) : + maker.Type(createVoidType(job.builderType.getSymbolTable(), CTC_VOID)); }}; StatementMaker returnStatementMaker = new StatementMaker() { @Override public JCStatement make() { - return chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; + return job.oldChain ? maker.Return(maker.Ident(job.builderType.toName("this"))) : null; }}; - generateMethods(cfv, data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); + generateMethods(job.checkerFramework, data, deprecate, job.builderType, job.sourceNode, job.oldFluent, returnTypeMaker, returnStatementMaker, job.accessInners); } /** * Generates the singular, plural, and clear methods for the given {@link SingularData}. * Uses the given {@code returnTypeMaker} and {@code returnStatementMaker} for the generated methods. */ - public abstract void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access); + public abstract void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JavacNode source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access); - protected void doGenerateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + protected void doGenerateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JavacNode source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { JavacTreeMaker maker = builderType.getTreeMaker(); generateSingularMethod(cfv, deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent, access); generatePluralMethod(cfv, deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent, access); generateClearMethod(cfv, deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, access); } - private void finishAndInjectMethod(CheckerFrameworkVersion cfv, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean deprecate, ListBuffer<JCStatement> statements, Name methodName, List<JCVariableDecl> jcVariableDecls, List<JCAnnotation> methodAnnotations, AccessLevel access, Boolean ignoreNullCollections) { + private void finishAndInjectMethod(CheckerFrameworkVersion cfv, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JavacNode source, boolean deprecate, ListBuffer<JCStatement> statements, Name methodName, List<JCVariableDecl> jcVariableDecls, List<JCAnnotation> methodAnnotations, AccessLevel access, Boolean ignoreNullCollections) { if (returnStatement != null) statements.append(returnStatement); JCBlock body = maker.Block(0, statements.toList()); JCModifiers mods = makeMods(maker, cfv, builderType, deprecate, access, methodAnnotations); @@ -288,15 +290,15 @@ public class JavacSingularsRecipes { } JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, jcVariableDecls, thrown, body, null); - recursiveSetGeneratedBy(method, source, builderType.getContext()); if (returnStatement != null) createRelevantNonNullAnnotation(builderType, method); + recursiveSetGeneratedBy(method, source); injectMethod(builderType, method); } - private void generateClearMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, AccessLevel access) { + private void generateClearMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JavacNode source, AccessLevel access) { JCStatement clearStatement = generateClearStatements(maker, data, builderType); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); - statements.add(clearStatement); + statements.append(clearStatement); Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString())); finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, methodName, List.<JCVariableDecl>nil(), List.<JCAnnotation>nil(), access, null); @@ -304,7 +306,7 @@ public class JavacSingularsRecipes { protected abstract JCStatement generateClearStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType); - private void generateSingularMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent, AccessLevel access) { + private void generateSingularMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JavacNode source, boolean fluent, AccessLevel access) { ListBuffer<JCStatement> statements = generateSingularMethodStatements(maker, data, builderType, source); List<JCVariableDecl> params = generateSingularMethodParameters(maker, data, builderType, source); Name name = data.getSingularName(); @@ -317,7 +319,7 @@ public class JavacSingularsRecipes { finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, methodAnnotations, access, null); } - protected JCVariableDecl generateSingularMethodParameter(int typeIndex, JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source, Name name) { + protected JCVariableDecl generateSingularMethodParameter(int typeIndex, JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source, Name name) { long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext()); JCExpression type = cloneParamType(typeIndex, maker, data.getTypeArgs(), builderType, source); List<JCAnnotation> typeUseAnns = getTypeUseAnnotations(type); @@ -332,11 +334,11 @@ public class JavacSingularsRecipes { return maker.Exec(invokeAdd); } - protected abstract ListBuffer<JCStatement> generateSingularMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source); + protected abstract ListBuffer<JCStatement> generateSingularMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source); - protected abstract List<JCVariableDecl> generateSingularMethodParameters(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source); + protected abstract List<JCVariableDecl> generateSingularMethodParameters(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source); - private void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent, AccessLevel access) { + private void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JavacNode source, boolean fluent, AccessLevel access) { ListBuffer<JCStatement> statements = generatePluralMethodStatements(maker, data, builderType, source); Name name = data.getPluralName(); @@ -355,7 +357,7 @@ public class JavacSingularsRecipes { JCExpression incomingIsNotNull = maker.Binary(CTC_NOT_EQUAL, maker.Ident(data.getPluralName()), maker.Literal(CTC_BOT, null)); JCStatement onNotNull = maker.Block(0, statements.toList()); statements = new ListBuffer<JCStatement>(); - statements.add(maker.If(incomingIsNotNull, onNotNull, null)); + statements.append(maker.If(incomingIsNotNull, onNotNull, null)); } else { statements.prepend(JavacHandlerUtil.generateNullCheck(maker, null, data.getPluralName(), builderType, "%s cannot be null")); } @@ -365,7 +367,7 @@ public class JavacSingularsRecipes { finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, List.of(param), methodAnnotations, access, ignoreNullCollections); } - protected ListBuffer<JCStatement> generatePluralMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected ListBuffer<JCStatement> generatePluralMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName() + "All"); @@ -377,9 +379,9 @@ public class JavacSingularsRecipes { protected abstract JCExpression getPluralMethodParamType(JavacNode builderType); - protected abstract JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source); + protected abstract JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source); - public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable); + public abstract void appendBuildCode(SingularData data, JavacNode builderType, JavacNode source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable); public boolean shadowedDuringBuild() { return true; @@ -393,7 +395,7 @@ public class JavacSingularsRecipes { } } - public void appendCleaningCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements) { + public void appendCleaningCode(SingularData data, JavacNode builderType, JavacNode source, ListBuffer<JCStatement> statements) { } // -- Utility methods -- @@ -409,16 +411,15 @@ public class JavacSingularsRecipes { * @param typeArgs the list of type args to clone. * @param source The source annotation that is the root cause of this code generation. */ - protected JCExpression addTypeArgs(int count, boolean addExtends, JavacNode node, JCExpression type, List<JCExpression> typeArgs, JCTree source) { + protected JCExpression addTypeArgs(int count, boolean addExtends, JavacNode node, JCExpression type, List<JCExpression> typeArgs, JavacNode source) { JavacTreeMaker maker = node.getTreeMaker(); List<JCExpression> clonedAndFixedTypeArgs = createTypeArgs(count, addExtends, node, typeArgs, source); return maker.TypeApply(type, clonedAndFixedTypeArgs); } - protected List<JCExpression> createTypeArgs(int count, boolean addExtends, JavacNode node, List<JCExpression> typeArgs, JCTree source) { + protected List<JCExpression> createTypeArgs(int count, boolean addExtends, JavacNode node, List<JCExpression> typeArgs, JavacNode source) { JavacTreeMaker maker = node.getTreeMaker(); - Context context = node.getContext(); if (count < 0) throw new IllegalArgumentException("count is negative"); if (count == 0) return List.nil(); @@ -435,17 +436,17 @@ public class JavacSingularsRecipes { } catch (Exception e) { inner = genJavaLangTypeRef(node, "Object"); } - arguments.append(cloneType(maker, inner, source, context)); + arguments.append(cloneType(maker, inner, source)); } else { - arguments.append(cloneType(maker, orig, source, context)); + arguments.append(cloneType(maker, orig, source)); } } else { if (orig.getKind() == Kind.UNBOUNDED_WILDCARD || orig.getKind() == Kind.SUPER_WILDCARD) { arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null)); } else if (orig.getKind() == Kind.EXTENDS_WILDCARD) { - arguments.append(cloneType(maker, orig, source, context)); + arguments.append(cloneType(maker, orig, source)); } else { - arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), cloneType(maker, orig, source, context))); + arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), cloneType(maker, orig, source))); } } if (--count == 0) break; @@ -468,7 +469,7 @@ public class JavacSingularsRecipes { JCExpression fn = maker.Select(maker.Select(maker.Ident(thisName), name), builderType.toName("size")); JCExpression sizeInvoke = maker.Apply(List.<JCExpression>nil(), fn, List.<JCExpression>nil()); if (nullGuard) { - JCExpression isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(thisName), name), maker.Literal(CTC_BOT, 0)); + JCExpression isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(thisName), name), maker.Literal(CTC_BOT, null)); JCExpression out = maker.Conditional(isNull, maker.Literal(CTC_INT, 0), sizeInvoke); if (parens) return maker.Parens(out); return out; @@ -476,7 +477,7 @@ public class JavacSingularsRecipes { return sizeInvoke; } - protected JCExpression cloneParamType(int index, JavacTreeMaker maker, List<JCExpression> typeArgs, JavacNode builderType, JCTree source) { + protected JCExpression cloneParamType(int index, JavacTreeMaker maker, List<JCExpression> typeArgs, JavacNode builderType, JavacNode source) { if (typeArgs == null || typeArgs.size() <= index) { return genJavaLangTypeRef(builderType, "Object"); } else { @@ -485,12 +486,12 @@ public class JavacSingularsRecipes { return genJavaLangTypeRef(builderType, "Object"); } else if (originalType.getKind() == Kind.EXTENDS_WILDCARD) { try { - return cloneType(maker, (JCExpression) ((JCWildcard) originalType).inner, source, builderType.getContext()); + return cloneType(maker, (JCExpression) ((JCWildcard) originalType).inner, source); } catch (Exception e) { return genJavaLangTypeRef(builderType, "Object"); } } else { - return cloneType(maker, originalType, source, builderType.getContext()); + return cloneType(maker, originalType, source); } } } @@ -500,5 +501,11 @@ public class JavacSingularsRecipes { protected abstract int getTypeArgumentsCount(); protected abstract String getEmptyMaker(String target); + + public JCExpression getEmptyExpression(String target, JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { + String emptyMaker = getEmptyMaker(target); + List<JCExpression> typeArgs = createTypeArgs(getTypeArgumentsCount(), false, builderType, data.getTypeArgs(), source); + return maker.Apply(typeArgs, chainDots(builderType, emptyMaker.split("\\.")), List.<JCExpression>nil()); + } } } diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java index e0621cf7..84840799 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,10 +23,9 @@ package lombok.javac.handlers.singulars; import lombok.core.LombokImmutableList; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(JavacSingularizer.class) +@Provides(JavacSingularizer.class) public class JavacGuavaMapSingularizer extends JavacGuavaSingularizer { // TODO cgcc.ImmutableMultimap, cgcc.ImmutableListMultimap, cgcc.ImmutableSetMultimap // TODO cgcc.ImmutableClassToInstanceMap diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java index 5c7fcab5..44d6d15b 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,12 +21,11 @@ */ package lombok.javac.handlers.singulars; -import org.mangosdk.spi.ProviderFor; - import lombok.core.LombokImmutableList; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; +import lombok.spi.Provides; -@ProviderFor(JavacSingularizer.class) +@Provides(JavacSingularizer.class) public class JavacGuavaSetListSingularizer extends JavacGuavaSingularizer { // TODO com.google.common.collect.ImmutableRangeSet // TODO com.google.common.collect.ImmutableMultiset and com.google.common.collect.ImmutableSortedMultiset diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index 7cd676c0..ce5aad5e 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java @@ -39,7 +39,6 @@ import lombok.javac.handlers.JavacSingularsRecipes.SingularData; import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; @@ -62,7 +61,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return "builder"; } - @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) { + @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JavacNode source) { JavacTreeMaker maker = builderType.getTreeMaker(); String simpleTypeName = getSimpleTargetTypeName(data); JCExpression type = JavacHandlerUtil.chainDots(builderType, "com", "google", "common", "collect", simpleTypeName, "Builder"); @@ -72,7 +71,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JavacNode source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { doGenerateMethods(cfv, data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); } @@ -83,7 +82,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { } @Override - protected List<JCVariableDecl> generateSingularMethodParameters(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected List<JCVariableDecl> generateSingularMethodParameters(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { Name[] names = generateSingularMethodParameterNames(data, builderType); ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); for (int i = 0; i < names.length; i++) { @@ -93,7 +92,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { } @Override - protected ListBuffer<JCStatement> generateSingularMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected ListBuffer<JCStatement> generateSingularMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { Name[] names = generateSingularMethodParameterNames(data, builderType); JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName()); @@ -124,7 +123,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return genTypeRef(builderType, getAddAllTypeName()); } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JavacNode source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { JavacTreeMaker maker = builderType.getTreeMaker(); List<JCExpression> jceBlank = List.nil(); @@ -151,12 +150,12 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { JCExpression init = maker.Conditional(isNull, empty, invokeBuild); // this.pluralName == null ? ImmutableX.of() : this.pluralName.build() - JCStatement jcs = maker.VarDef(maker.Modifiers(0), data.getPluralName(), varType, init); + JCStatement jcs = maker.VarDef(maker.Modifiers(0L), data.getPluralName(), varType, init); statements.append(jcs); } @Override - protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { List<JCExpression> jceBlank = List.nil(); JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()); diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java index 080266b8..bdc5facc 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,12 +21,11 @@ */ package lombok.javac.handlers.singulars; -import org.mangosdk.spi.ProviderFor; - import lombok.core.LombokImmutableList; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; +import lombok.spi.Provides; -@ProviderFor(JavacSingularizer.class) +@Provides(JavacSingularizer.class) public class JavacGuavaTableSingularizer extends JavacGuavaSingularizer { private static final LombokImmutableList<String> SUFFIXES = LombokImmutableList.of("rowKey", "columnKey", "value"); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java index 634a086d..dfc8c7b0 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java @@ -37,7 +37,6 @@ import lombok.javac.handlers.JavacSingularsRecipes.SingularData; import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; @@ -58,7 +57,7 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize return super.listMethodsToBeGenerated(data, builderType); } - @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) { + @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JavacNode source) { JavacTreeMaker maker = builderType.getTreeMaker(); JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList"); type = addTypeArgs(1, false, builderType, type, data.getTypeArgs(), source); @@ -67,7 +66,7 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JavacNode source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { doGenerateMethods(cfv, data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); } @@ -84,13 +83,13 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize } @Override - protected ListBuffer<JCStatement> generateSingularMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected ListBuffer<JCStatement> generateSingularMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { return new ListBuffer<JCStatement>() .append(generateSingularMethodAddStatement(maker, builderType, data.getSingularName(), data.getPluralName().toString())); } @Override - protected List<JCVariableDecl> generateSingularMethodParameters(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected List<JCVariableDecl> generateSingularMethodParameters(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { JCVariableDecl param = generateSingularMethodParameter(0, maker, data, builderType, source, data.getSingularName()); return List.of(param); } @@ -101,7 +100,7 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize } @Override - protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { return createConstructBuilderVarIfNeeded(maker, data, builderType, false, source); } diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java index b4ad3428..10f1ddd1 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,15 +24,13 @@ package lombok.javac.handlers.singulars; import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; -import org.mangosdk.spi.ProviderFor; - import lombok.core.LombokImmutableList; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; +import lombok.spi.Provides; -import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCStatement; @@ -40,7 +38,7 @@ import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; -@ProviderFor(JavacSingularizer.class) +@Provides(JavacSingularizer.class) public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingularizer { @Override public LombokImmutableList<String> getSupportedTypes() { return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.lang.Iterable"); @@ -50,7 +48,7 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari return "java.util.Collections.emptyList"; } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JavacNode source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { JavacTreeMaker maker = builderType.getTreeMaker(); List<JCExpression> jceBlank = List.nil(); ListBuffer<JCCase> cases = new ListBuffer<JCCase>(); @@ -89,12 +87,12 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true, false, builderVariable), cases.toList()); JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn()); localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs(), source); - JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null); + JCStatement varDefStat = maker.VarDef(maker.Modifiers(0L), data.getPluralName(), localShadowerType, null); statements.append(varDefStat); statements.append(switchStat); } - private List<JCStatement> createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source, String builderVariable) { + private List<JCStatement> createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source, String builderVariable) { List<JCExpression> jceBlank = List.nil(); Name thisName = builderType.toName(builderVariable); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index 8dc7ecff..b8c931c7 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,11 +36,9 @@ import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCStatement; @@ -49,7 +47,7 @@ import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; -@ProviderFor(JavacSingularizer.class) +@Provides(JavacSingularizer.class) public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { @Override public LombokImmutableList<String> getSupportedTypes() { return LombokImmutableList.of("java.util.Map", "java.util.SortedMap", "java.util.NavigableMap"); @@ -74,7 +72,7 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { return super.listMethodsToBeGenerated(data, builderType); } - @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) { + @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JavacNode source) { JavacTreeMaker maker = builderType.getTreeMaker(); JCVariableDecl buildKeyField; { @@ -98,7 +96,7 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { + @Override public void generateMethods(CheckerFrameworkVersion cfv, SingularData data, boolean deprecate, JavacNode builderType, JavacNode source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) { doGenerateMethods(cfv, data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker, access); } @@ -117,7 +115,7 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { } @Override - protected ListBuffer<JCStatement> generateSingularMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected ListBuffer<JCStatement> generateSingularMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { Name keyName = builderType.toName(data.getSingularName().toString() + "Key"); Name valueName = builderType.toName(data.getSingularName().toString() + "Value"); @@ -130,7 +128,7 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { } @Override - protected List<JCVariableDecl> generateSingularMethodParameters(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected List<JCVariableDecl> generateSingularMethodParameters(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { Name keyName = builderType.toName(data.getSingularName().toString() + "Key"); Name valueName = builderType.toName(data.getSingularName().toString() + "Value"); JCVariableDecl paramKey = generateSingularMethodParameter(0, maker, data, builderType, source, keyName); @@ -139,7 +137,7 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { } @Override - protected ListBuffer<JCStatement> generatePluralMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected ListBuffer<JCStatement> generatePluralMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { List<JCExpression> jceBlank = List.nil(); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); long baseFlags = JavacHandlerUtil.addFinalIfNeeded(0, builderType.getContext()); @@ -164,11 +162,11 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { } @Override - protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, JavacNode source) { return createConstructBuilderVarIfNeeded(maker, data, builderType, true, source); } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JavacNode source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { JavacTreeMaker maker = builderType.getTreeMaker(); if (data.getTargetFqn().equals("java.util.Map")) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java index 7c870c0a..6d2e0655 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,20 +21,18 @@ */ package lombok.javac.handlers.singulars; -import org.mangosdk.spi.ProviderFor; - import lombok.core.LombokImmutableList; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; +import lombok.spi.Provides; -import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; -@ProviderFor(JavacSingularizer.class) +@Provides(JavacSingularizer.class) public class JavacJavaUtilSetSingularizer extends JavacJavaUtilListSetSingularizer { @Override public LombokImmutableList<String> getSupportedTypes() { return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet"); @@ -46,7 +44,7 @@ public class JavacJavaUtilSetSingularizer extends JavacJavaUtilListSetSingulariz return "java.util.Collections.emptySet"; } - @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JavacNode source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) { JavacTreeMaker maker = builderType.getTreeMaker(); if (data.getTargetFqn().equals("java.util.Set")) { diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java index 50950915..3b6113df 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java @@ -24,7 +24,6 @@ package lombok.javac.handlers.singulars; import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; -import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCStatement; @@ -38,7 +37,7 @@ import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; abstract class JavacJavaUtilSingularizer extends JavacSingularizer { - protected List<JCStatement> createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source, String builderVariable) { + protected List<JCStatement> createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JavacNode source, String builderVariable) { List<JCExpression> jceBlank = List.nil(); ListBuffer<JCCase> cases = new ListBuffer<JCCase>(); @@ -84,13 +83,13 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true, false, builderVariable), cases.toList()); JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn()); localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source); - JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null); + JCStatement varDefStat = maker.VarDef(maker.Modifiers(0L), data.getPluralName(), localShadowerType, null); return List.of(varDefStat, switchStat); } - protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, JCTree source) { + protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, JavacNode source) { List<JCExpression> jceBlank = List.nil(); - + Name v1Name = mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(); Name v2Name = mapMode ? builderType.toName(data.getPluralName() + "$value") : null; JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), v1Name); @@ -117,7 +116,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { return maker.If(cond, thenPart, null); } - protected List<JCStatement> createJavaUtilSimpleCreationAndFillStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, JCTree source, String builderVariable) { + protected List<JCStatement> createJavaUtilSimpleCreationAndFillStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, JavacNode source, String builderVariable) { List<JCExpression> jceBlank = List.nil(); Name thisName = builderType.toName(builderVariable); @@ -143,7 +142,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { if (defineVar) { JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn()); localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source); - createStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, constructorCall); + createStat = maker.VarDef(maker.Modifiers(0L), data.getPluralName(), localShadowerType, constructorCall); } else { createStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), constructorCall)); } @@ -161,7 +160,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { // error: method put in interface Map<K#2,V#2> cannot be applied to given types; arg2 = maker.TypeCast(createTypeArgs(2, false, builderType, data.getTypeArgs(), source).get(1), arg2); JCStatement putStatement = maker.Exec(maker.Apply(jceBlank, pluralnameDotPut, List.of(arg1, arg2))); - JCStatement forInit = maker.VarDef(maker.Modifiers(0), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0)); + JCStatement forInit = maker.VarDef(maker.Modifiers(0L), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0)); JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true, builderVariable)); JCExpression incrementExpr = maker.Unary(CTC_POSTINC, maker.Ident(ivar)); fillStat = maker.ForLoop(List.of(forInit), checkExpr, List.of(maker.Exec(incrementExpr)), putStatement); diff --git a/src/core9/module-info.java b/src/core9/module-info.java index 293bc839..8e8bd891 100644 --- a/src/core9/module-info.java +++ b/src/core9/module-info.java @@ -27,11 +27,12 @@ module lombok { exports lombok; exports lombok.experimental; exports lombok.extern.apachecommons; + exports lombok.extern.flogger; + exports lombok.extern.jackson; exports lombok.extern.java; exports lombok.extern.jbosslog; exports lombok.extern.log4j; exports lombok.extern.slf4j; - exports lombok.extern.flogger; exports lombok.launch to lombok.mapstruct; diff --git a/src/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java index a1fd0e56..10ec5015 100755 --- a/src/delombok/lombok/delombok/Delombok.java +++ b/src/delombok/lombok/delombok/Delombok.java @@ -34,7 +34,6 @@ import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URLDecoder; @@ -44,6 +43,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.List; @@ -52,6 +52,7 @@ import java.util.Map; import java.util.Set; import java.util.regex.Pattern; +import javax.annotation.processing.AbstractProcessor; import javax.tools.DiagnosticListener; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; @@ -98,6 +99,7 @@ public class Delombok { private LinkedHashMap<File, File> fileToBase = new LinkedHashMap<File, File>(); private List<File> filesToParse = new ArrayList<File>(); private Map<String, String> formatPrefs = new HashMap<String, String>(); + private List<AbstractProcessor> additionalAnnotationProcessors = new ArrayList<AbstractProcessor>(); /** If null, output to standard out. */ private File output = null; @@ -166,6 +168,10 @@ public class Delombok { private boolean help; } + static { + LombokProcessor.addOpensForLombok(); + } + private static String indentAndWordbreak(String in, int indent, int maxLen) { StringBuilder out = new StringBuilder(); StringBuilder line = new StringBuilder(); @@ -643,6 +649,10 @@ public class Delombok { fileToBase.put(f, base); } + public void addAdditionalAnnotationProcessor(AbstractProcessor processor) { + additionalAnnotationProcessors.add(processor); + } + private static <T> com.sun.tools.javac.util.List<T> toJavacList(List<T> list) { com.sun.tools.javac.util.List<T> out = com.sun.tools.javac.util.List.nil(); ListIterator<T> li = list.listIterator(list.size()); @@ -701,8 +711,12 @@ public class Delombok { if (!disablePreview && Javac.getJavaCompilerVersion() >= 11) argsList.add("--enable-preview"); - String[] argv = argsList.toArray(new String[0]); - args.init("javac", argv); + if (Javac.getJavaCompilerVersion() < 15) { + String[] argv = argsList.toArray(new String[0]); + args.init("javac", argv); + } else { + args.init("javac", argsList); + } options.put("diags.legacy", "TRUE"); options.put("allowStringFolding", "FALSE"); } else { @@ -715,7 +729,9 @@ public class Delombok { List<JCCompilationUnit> roots = new ArrayList<JCCompilationUnit>(); Map<JCCompilationUnit, File> baseMap = new IdentityHashMap<JCCompilationUnit, File>(); - Set<LombokProcessor> processors = Collections.singleton(new lombok.javac.apt.LombokProcessor()); + Set<AbstractProcessor> processors = new HashSet<AbstractProcessor>(); + processors.add(new lombok.javac.apt.LombokProcessor()); + processors.addAll(additionalAnnotationProcessors); if (Javac.getJavaCompilerVersion() >= 9) { JavaFileManager jfm_ = context.get(JavaFileManager.class); @@ -837,12 +853,8 @@ public class Delombok { } } } - try { - return attributeMethod.invoke(compiler, arg); - } catch (Exception e) { - if (e instanceof InvocationTargetException) throw Lombok.sneakyThrow(e.getCause()); - throw Lombok.sneakyThrow(e); - } + + return Permit.invokeSneaky(attributeMethod, compiler, arg); } private static Method flowMethod; @@ -859,12 +871,8 @@ public class Delombok { } } } - try { - flowMethod.invoke(compiler, arg); - } catch (Exception e) { - if (e instanceof InvocationTargetException) throw Lombok.sneakyThrow(e.getCause()); - throw Lombok.sneakyThrow(e); - } + + Permit.invokeSneaky(flowMethod, compiler, arg); } private static String canonical(File dir) { diff --git a/src/delombok/lombok/delombok/DelombokApp.java b/src/delombok/lombok/delombok/DelombokApp.java index da1975b4..f5a5fc81 100644 --- a/src/delombok/lombok/delombok/DelombokApp.java +++ b/src/delombok/lombok/delombok/DelombokApp.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,10 +37,9 @@ import java.util.jar.JarFile; import lombok.core.LombokApp; import lombok.permit.Permit; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(LombokApp.class) +@Provides public class DelombokApp extends LombokApp { @Override public int runApp(List<String> args) throws Exception { try { @@ -53,11 +52,11 @@ public class DelombokApp extends LombokApp { return 1; } try { - Permit.getMethod(loadDelombok(args), "main", String[].class).invoke(null, new Object[] {args.toArray(new String[0])}); + Permit.invoke(Permit.getMethod(loadDelombok(args), "main", String[].class), null, new Object[] {args.toArray(new String[0])}); } catch (InvocationTargetException e1) { Throwable t = e1.getCause(); - if (t instanceof Error) throw (Error)t; - if (t instanceof Exception) throw (Exception)t; + if (t instanceof Error) throw (Error) t; + if (t instanceof Exception) throw (Exception) t; throw e1; } return 0; diff --git a/src/delombok/lombok/delombok/DocCommentIntegrator.java b/src/delombok/lombok/delombok/DocCommentIntegrator.java index bab0abd8..e61968a5 100644 --- a/src/delombok/lombok/delombok/DocCommentIntegrator.java +++ b/src/delombok/lombok/delombok/DocCommentIntegrator.java @@ -89,7 +89,7 @@ public class DocCommentIntegrator { ((Map<JCTree, String>) map_).put(node, docCommentContent); return true; } else if (Javac.instanceOfDocCommentTable(map_)) { - CommentAttacher_8.attach(node, docCommentContent, map_); + CommentAttacher_8.attach(node, docCommentContent, cmt.pos, map_); return true; } @@ -98,7 +98,7 @@ public class DocCommentIntegrator { /* Container for code which will cause class loader exceptions on javac below 8. By being in a separate class, we avoid the problem. */ private static class CommentAttacher_8 { - static void attach(final JCTree node, String docCommentContent, Object map_) { + static void attach(final JCTree node, String docCommentContent, final int pos, Object map_) { final String docCommentContent_ = docCommentContent; ((DocCommentTable) map_).putComment(node, new Comment() { @Override public String getText() { @@ -106,7 +106,7 @@ public class DocCommentIntegrator { } @Override public int getSourcePos(int index) { - return -1; + return pos + index; } @Override public CommentStyle getStyle() { diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java index 6682b5a3..05904815 100644 --- a/src/delombok/lombok/delombok/PrettyPrinter.java +++ b/src/delombok/lombok/delombok/PrettyPrinter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2020 The Project Lombok Authors. + * Copyright (C) 2016-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,6 @@ import static lombok.javac.JavacTreeMaker.TypeTag.typeTag; import java.io.IOException; import java.io.Writer; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; @@ -97,7 +96,6 @@ import lombok.javac.PackageName; import lombok.permit.Permit; import lombok.javac.CommentInfo.EndConnection; import lombok.javac.CommentInfo.StartConnection; -import lombok.javac.Java14Flags; import lombok.javac.JavacTreeMaker.TreeTag; import lombok.javac.JavacTreeMaker.TypeTag; @@ -280,7 +278,6 @@ public class PrettyPrinter extends JCTree.Visitor { aligned = false; } - private void println() { try { out.write(LINE_SEP); @@ -517,7 +514,7 @@ public class PrettyPrinter extends JCTree.Visitor { boolean isInterface = (tree.mods.flags & INTERFACE) != 0; boolean isAnnotationInterface = isInterface && (tree.mods.flags & ANNOTATION) != 0; boolean isEnum = (tree.mods.flags & ENUM) != 0; - boolean isRecord = (tree.mods.flags & Java14Flags.RECORD) != 0; + boolean isRecord = (tree.mods.flags & RECORD) != 0; if (isAnnotationInterface) print("@interface "); else if (isInterface) print("interface "); @@ -545,9 +542,7 @@ public class PrettyPrinter extends JCTree.Visitor { print(tree.implementing, ", "); } - if (isRecord) { - printRecordConstructor(tree.defs); - } + if (isRecord) printRecordConstructor(tree.defs); println(" {"); indent++; @@ -564,7 +559,7 @@ public class PrettyPrinter extends JCTree.Visitor { for (JCTree member : members) { if (member instanceof JCVariableDecl) { JCVariableDecl variableDecl = (JCVariableDecl) member; - if ((variableDecl.mods.flags & Java14Flags.GENERATED_MEMBER) != 0) { + if ((variableDecl.mods.flags & GENERATED_MEMBER) != 0) { if (!first) print(", "); first = false; printAnnotations(variableDecl.mods.annotations, false); @@ -574,7 +569,7 @@ public class PrettyPrinter extends JCTree.Visitor { } print(")"); } - + private void printClassMembers(List<JCTree> members, boolean isEnum, boolean isInterface) { Class<?> prefType = null; int typeOfPrevEnumMember = isEnum ? 3 : 0; // 1 = normal, 2 = with body, 3 = no enum field yet. @@ -604,7 +599,7 @@ public class PrettyPrinter extends JCTree.Visitor { JCTree init = ((JCVariableDecl) member).init; typeOfPrevEnumMember = init instanceof JCNewClass && ((JCNewClass) init).def != null ? 2 : 1; } else if (member instanceof JCVariableDecl) { - if ((((JCVariableDecl) member).mods.flags & Java14Flags.GENERATED_MEMBER) != 0) continue; + if ((((JCVariableDecl) member).mods.flags & GENERATED_MEMBER) != 0) continue; if (prefType != null && prefType != JCVariableDecl.class) println(); if (isInterface) flagMod = -1L & ~(PUBLIC | STATIC | FINAL); print(member); @@ -1412,9 +1407,10 @@ public class PrettyPrinter extends JCTree.Visitor { } void printBindingPattern(JCTree tree) { - print((JCExpression) readObject(tree, "vartype", null)); + JCTree var = readObject(tree, "var", tree); + print((JCExpression) readObject(var, "vartype", null)); print(" "); - print((Name) readObject(tree, "name", null)); + print((Name) readObject(var, "name", null)); } @Override public void visitTry(JCTry tree) { @@ -1572,13 +1568,7 @@ public class PrettyPrinter extends JCTree.Visitor { } public static JCTree getExtendsClause(JCClassDecl decl) { - try { - return (JCTree) getExtendsClause.invoke(decl); - } catch (IllegalAccessException e) { - throw sneakyThrow(e); - } catch (InvocationTargetException e) { - throw sneakyThrow(e.getCause()); - } + return (JCTree) Permit.invokeSneaky(getExtendsClause, decl); } static RuntimeException sneakyThrow(Throwable t) { diff --git a/src/delombok/lombok/delombok/ant/DelombokTask.java b/src/delombok/lombok/delombok/ant/DelombokTask.java index defd1709..7c585836 100644 --- a/src/delombok/lombok/delombok/ant/DelombokTask.java +++ b/src/delombok/lombok/delombok/ant/DelombokTask.java @@ -177,9 +177,11 @@ class Tasks { } return Class.forName(name, true, shadowLoader); - } catch (Exception e) { - if (e instanceof RuntimeException) throw (RuntimeException) e; - throw new RuntimeException(e); + } catch (Throwable t) { + if (t instanceof InvocationTargetException) t = t.getCause(); + if (t instanceof RuntimeException) throw (RuntimeException) t; + if (t instanceof Error) throw (Error) t; + throw new RuntimeException(t); } } @@ -207,10 +209,10 @@ class Tasks { Method m = instance.getClass().getMethod("execute", Location.class); m.invoke(instance, loc); - } catch (Exception e) { - Throwable t = (e instanceof InvocationTargetException) ? e.getCause() : e; - if (t instanceof Error) throw (Error) t; + } catch (Throwable t) { + if (t instanceof InvocationTargetException) t = t.getCause(); if (t instanceof RuntimeException) throw (RuntimeException) t; + if (t instanceof Error) throw (Error) t; throw new RuntimeException(t); } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java index aa01c13d..25906c6b 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java @@ -31,6 +31,12 @@ import lombok.patcher.scripts.ScriptBuilder; public class EclipseLoaderPatcher { private static final String TRANSPLANTS_CLASS_NAME = "lombok.eclipse.agent.EclipseLoaderPatcherTransplants"; + static final String[] OSGI_TYPES = { + "org/eclipse/osgi/internal/baseadaptor/DefaultClassLoader", + "org/eclipse/osgi/framework/adapter/core/AbstractClassLoader", + "org/eclipse/osgi/internal/loader/ModuleClassLoader" + }; + public static void patchEquinoxLoaders(ScriptManager sm, Class<?> launchingContext) { sm.addScript(ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader", "loadClass", diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java index c560f002..ce26c892 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,6 +22,7 @@ package lombok.eclipse.agent; import static lombok.patcher.scripts.ScriptBuilder.*; +import static lombok.eclipse.agent.EclipseLoaderPatcher.OSGI_TYPES; import java.io.File; import java.lang.instrument.Instrumentation; @@ -49,34 +50,11 @@ import lombok.patcher.scripts.ScriptBuilder; * transformed. */ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { - // At some point I'd like the agent to be capable of auto-detecting if its on eclipse or on ecj. This class is a sure sign we're not in ecj but in eclipse. -ReinierZ - @SuppressWarnings("unused") - private static final String ECLIPSE_SIGNATURE_CLASS = "org/eclipse/core/runtime/adaptor/EclipseStarter"; - @Override public void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected, Class<?> launchingContext) throws Exception { - String[] args = agentArgs == null ? new String[0] : agentArgs.split(":"); - boolean forceEcj = false; - boolean forceEclipse = false; - - for (String arg : args) { - if (arg.trim().equalsIgnoreCase("ECJ")) forceEcj = true; - if (arg.trim().equalsIgnoreCase("ECLIPSE")) forceEclipse = true; - } - if (forceEcj && forceEclipse) { - forceEcj = false; - forceEclipse = false; - } - - boolean ecj; - - if (forceEcj) ecj = true; - else if (forceEclipse) ecj = false; - else ecj = injected; - - registerPatchScripts(instrumentation, injected, ecj, launchingContext); + registerPatchScripts(instrumentation, injected, launchingContext); } - private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, boolean ecjOnly, Class<?> launchingContext) { + private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, Class<?> launchingContext) { ScriptManager sm = new ScriptManager(); sm.registerTransformer(instrumentation); sm.setFilter(new Filter() { @@ -97,33 +75,33 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } }); - if (!ecjOnly) { - EclipseLoaderPatcher.patchEquinoxLoaders(sm, launchingContext); - patchCatchReparse(sm); - patchIdentifierEndReparse(sm); - patchRetrieveEllipsisStartPosition(sm); - patchRetrieveRightBraceOrSemiColonPosition(sm); - patchRetrieveProperRightBracketPosition(sm); - patchSetGeneratedFlag(sm); - patchDomAstReparseIssues(sm); - patchHideGeneratedNodes(sm); - patchPostCompileHookEclipse(sm); - patchFixSourceTypeConverter(sm); - patchDisableLombokForCodeCleanup(sm); - patchListRewriteHandleGeneratedMethods(sm); - patchSyntaxAndOccurrencesHighlighting(sm); - patchSortMembersOperation(sm); - patchExtractInterface(sm); - patchAboutDialog(sm); - patchEclipseDebugPatches(sm); - } else { - patchPostCompileHookEcj(sm); - } + EclipseLoaderPatcher.patchEquinoxLoaders(sm, launchingContext); + patchCatchReparse(sm); + patchIdentifierEndReparse(sm); + patchRetrieveEllipsisStartPosition(sm); + patchRetrieveRightBraceOrSemiColonPosition(sm); + patchRetrieveProperRightBracketPosition(sm); + patchRetrieveStartBlockPosition(sm); + patchSetGeneratedFlag(sm); + patchDomAstReparseIssues(sm); + patchHideGeneratedNodes(sm); + patchPostCompileHookEclipse(sm); + patchFixSourceTypeConverter(sm); + patchDisableLombokForCodeCleanup(sm); + patchListRewriteHandleGeneratedMethods(sm); + patchSyntaxAndOccurrencesHighlighting(sm); + patchSortMembersOperation(sm); + patchExtractInterface(sm); + patchAboutDialog(sm); + patchEclipseDebugPatches(sm); + patchJavadoc(sm); + + patchPostCompileHookEcj(sm); patchAvoidReparsingGeneratedCode(sm); patchLombokizeAST(sm); - patchEcjTransformers(sm, ecjOnly); - patchExtensionMethod(sm, ecjOnly); + patchEcjTransformers(sm); + patchExtensionMethod(sm); patchRenameField(sm); patchNullCheck(sm); @@ -158,7 +136,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { private static void patchExtractInterface(ScriptManager sm) { /* Fix sourceEnding for generated nodes to avoid null pointer */ - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.compiler.SourceElementNotifier", "notifySourceElementRequestor", "void", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration", "org.eclipse.jdt.internal.compiler.ast.TypeDeclaration", "org.eclipse.jdt.internal.compiler.ast.ImportReference")) .methodToWrap(new Hook("org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt", "get", "int", "java.lang.Object")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getSourceEndFixed", "int", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) @@ -166,7 +144,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .transplant().build()); /* Make sure the generated source element is found instead of the annotation */ - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMethodDeclaration", "void", "org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite", "org.eclipse.jdt.core.dom.rewrite.ASTRewrite", @@ -179,7 +157,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .transplant().build()); /* get real generated node in stead of a random one generated by the annotation */ - sm.addScript(ScriptBuilder.replaceMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.replaceMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMemberDeclarations")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMethodComments")) .methodToReplace(new Hook("org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil", "getMethodDeclarationNode", "org.eclipse.jdt.core.dom.MethodDeclaration", "org.eclipse.jdt.core.IMethod", "org.eclipse.jdt.core.dom.CompilationUnit")) @@ -187,14 +165,14 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .transplant().build()); /* Do not add @Override's for generated methods */ - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ListRewrite", "insertFirst")) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isListRewriteOnGeneratedNode", "boolean", "org.eclipse.jdt.core.dom.rewrite.ListRewrite")) .request(StackRequest.THIS) .transplant().build()); /* Do not add comments for generated methods */ - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMethodComment")) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode")) .request(StackRequest.PARAM2) @@ -207,7 +185,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { * This is doable without patching, but we intentionally patch it so that presence of the lombok info * in the about dialog can be used to ascertain that patching in general is doing something. */ - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.core.internal.runtime.Product", "getProperty", "java.lang.String", "java.lang.String")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "addLombokNotesToEclipseAboutDialog", "java.lang.String", "java.lang.String", "java.lang.String")) .request(StackRequest.RETURN_VALUE, StackRequest.PARAM1) @@ -218,7 +196,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { /* * Skip generated nodes for "visual effects" (syntax highlighting && highlight occurrences) */ - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.internal.ui.search.OccurrencesFinder", "addUsage")) .target(new MethodTarget("org.eclipse.jdt.internal.ui.search.OccurrencesFinder", "addWrite")) .target(new MethodTarget("org.eclipse.jdt.internal.ui.javaeditor.SemanticHighlightingReconciler$PositionCollector", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName")) @@ -229,7 +207,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchDisableLombokForCodeCleanup(ScriptManager sm) { - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.DoStatement")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.EnhancedForStatement")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.ForStatement")) @@ -256,7 +234,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchListRewriteHandleGeneratedMethods(ScriptManager sm) { - sm.addScript(ScriptBuilder.replaceMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.replaceMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteAnalyzer$ListRewriter", "rewriteList")) .methodToReplace(new Hook("org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent", "getChildren", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent[]")) .replacementMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "listRewriteHandleGeneratedMethods", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent[]", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent")) @@ -268,37 +246,37 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { * I would have liked to patch sortMembers, but kept getting a VerifyError: Illegal type in constant pool * So now I just patch all calling methods */ - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.CompilationUnit")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.CompilationUnit", "types", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.AnnotationTypeDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.AnnotationTypeDeclaration", "bodyDeclarations", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.AnonymousClassDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.AnonymousClassDeclaration", "bodyDeclarations", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.TypeDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.TypeDeclaration", "bodyDeclarations", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.EnumDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.EnumDeclaration", "bodyDeclarations", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.EnumDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.EnumDeclaration", "enumConstants", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) @@ -306,7 +284,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchDomAstReparseIssues(ScriptManager sm) { - sm.addScript(ScriptBuilder.replaceMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.replaceMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteAnalyzer", "visit")) .methodToReplace(new Hook("org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner", "getTokenEndOffset", "int", "int", "int")) .replacementMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getTokenEndOffsetFixed", "int", "org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner", "int", "int", "java.lang.Object")) @@ -317,7 +295,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchPostCompileHookEclipse(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder", "writeClassFileContents")) .target(new MethodTarget("org.eclipse.jdt.internal.core.builder.AbstractImageBuilder", "writeClassFileContents")) .methodToWrap(new Hook("org.eclipse.jdt.internal.compiler.ClassFile", "getBytes", "byte[]")) @@ -327,13 +305,13 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchPostCompileHookEcj(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfNotWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.compiler.tool.EclipseCompilerImpl", "outputClassFiles")) .methodToWrap(new Hook("javax.tools.JavaFileObject", "openOutputStream", "java.io.OutputStream")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "runPostCompiler", "java.io.OutputStream", "java.io.OutputStream")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfNotWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.compiler.util.Util", "writeToDisk")) .methodToWrap(new Hook("java.io.BufferedOutputStream", "<init>", "void", "java.io.OutputStream", "int")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "runPostCompiler", "java.io.BufferedOutputStream", "java.io.BufferedOutputStream", "java.lang.String", "java.lang.String")) @@ -342,7 +320,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchHideGeneratedNodes(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder", "findByNode")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder", "findByBinding")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedSimpleNames", "org.eclipse.jdt.core.dom.SimpleName[]", @@ -354,37 +332,37 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchFormatters(ScriptManager sm) { - // before Eclipse Mars - sm.addScript(ScriptBuilder.setSymbolDuringMethodCall() - .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "formatCompilationUnit")) - .callToWrap(new Hook("org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil", "parseCompilationUnit", "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration", "char[]", "java.util.Map", "boolean")) - .symbol("lombok.disable") - .build()); + // before Eclipse Mars + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.setSymbolDuringMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "formatCompilationUnit")) + .callToWrap(new Hook("org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil", "parseCompilationUnit", "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration", "char[]", "java.util.Map", "boolean")) + .symbol("lombok.disable") + .build()); // Eclipse Mars and beyond - sm.addScript(ScriptBuilder.setSymbolDuringMethodCall() - .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "parseSourceCode")) - .callToWrap(new Hook("org.eclipse.jdt.core.dom.ASTParser", "createAST", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.core.runtime.IProgressMonitor")) - .symbol("lombok.disable") - .build()); + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.setSymbolDuringMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "parseSourceCode")) + .callToWrap(new Hook("org.eclipse.jdt.core.dom.ASTParser", "createAST", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.core.runtime.IProgressMonitor")) + .symbol("lombok.disable") + .build()); } private static void patchRefactorScripts(ScriptManager sm) { - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ASTRewrite", "replace")) .target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ASTRewrite", "remove")) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "skipRewritingGeneratedNodes", "boolean", "org.eclipse.jdt.core.dom.ASTNode")) .transplant().request(StackRequest.PARAM1).build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameTypeProcessor", "addConstructorRenames")) .methodToWrap(new Hook("org.eclipse.jdt.core.IType", "getMethods", "org.eclipse.jdt.core.IMethod[]")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedMethods", "org.eclipse.jdt.core.IMethod[]", "org.eclipse.jdt.core.IMethod[]")) .transplant().build()); - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameAnalyzeUtil$ProblemNodeFinder$NameNodeVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName")) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode")) @@ -401,21 +379,28 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchIdentifierEndReparse(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveIdentifierEndPosition")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveIdentifierEndPosition", "int", "int", "int", "int")) .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM1, StackRequest.PARAM2).build()); } private static void patchRetrieveEllipsisStartPosition(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveEllipsisStartPosition")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveEllipsisStartPosition", "int", "int", "int")) .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build()); } + private static void patchRetrieveStartBlockPosition(ScriptManager sm) { + sm.addScriptIfWitness(OSGI_TYPES, 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() + sm.addScriptIfWitness(OSGI_TYPES, 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")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition", "int", "int", "int")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration")) @@ -447,28 +432,36 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchRetrieveProperRightBracketPosition(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, 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.addScriptIfWitness(OSGI_TYPES, 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()); } private static void patchSetGeneratedFlag(ScriptManager sm) { - sm.addScript(ScriptBuilder.addField() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() .targetClass("org.eclipse.jdt.internal.compiler.ast.ASTNode") .fieldName("$generatedBy") .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;") .setPublic().setTransient().build()); - sm.addScript(ScriptBuilder.addField() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() .targetClass("org.eclipse.jdt.core.dom.ASTNode") .fieldName("$isGenerated").fieldType("Z") .setPublic().setTransient().build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new TargetMatcher() { @Override public boolean matches(String classSpec, String methodName, String descriptor) { if (!"convert".equals(methodName)) return false; @@ -480,6 +473,10 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { return true; } + @Override public String describe() { + return "ASTConverter:[all relevant]"; + } + @Override public Collection<String> getAffectedClasses() { return Collections.singleton("org.eclipse.jdt.core.dom.ASTConverter"); } @@ -488,14 +485,14 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration")) .request(StackRequest.PARAM2, StackRequest.RETURN_VALUE) .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.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToFieldDeclaration", "org.eclipse.jdt.core.dom.FieldDeclaration", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration")) /* Targets beneath are only patched because the resulting dom nodes should be marked if generated. * However I couldn't find a usecase where these were actually used @@ -507,6 +504,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", @@ -514,7 +513,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .transplant().build()); /* Set generated flag for SimpleNames */ - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new TargetMatcher() { @Override public boolean matches(String classSpec, String methodName, String descriptor) { if (!methodName.startsWith("convert")) return false; @@ -526,6 +525,10 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { return true; } + @Override public String describe() { + return "ASTConverter::(all relevant)"; + } + @Override public Collection<String> getAffectedClasses() { return Collections.singleton("org.eclipse.jdt.core.dom.ASTConverter"); } @@ -535,16 +538,53 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, 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")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM2) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void", "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); + + + sm.addScriptIfWitness(OSGI_TYPES, 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.addScriptIfWitness(OSGI_TYPES, 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.addScriptIfWitness(OSGI_TYPES, 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.addScriptIfWitness(OSGI_TYPES, 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() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM4) @@ -552,7 +592,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM4) @@ -560,7 +600,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM3) @@ -568,7 +608,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM3) @@ -576,7 +616,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setTypeNameForAnnotation", "void", "org.eclipse.jdt.internal.compiler.ast.Annotation", "org.eclipse.jdt.core.dom.Annotation")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM1) @@ -584,7 +624,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setTypeNameForAnnotation", "void", "org.eclipse.jdt.internal.compiler.ast.Annotation", "org.eclipse.jdt.core.dom.Annotation")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM1) @@ -629,25 +669,26 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String PARSER_SIG = "org.eclipse.jdt.internal.compiler.parser.Parser"; final String CUD_SIG = "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration"; + final String OBJECT_SIG = "java.lang.Object"; sm.addScript(ScriptBuilder.wrapReturnValue() .target(new MethodTarget(PARSER_SIG, "getMethodBodies", "void", CUD_SIG)) - .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform", "void", PARSER_SIG, CUD_SIG)) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform", "void", OBJECT_SIG, OBJECT_SIG)) .request(StackRequest.THIS, StackRequest.PARAM1).build()); sm.addScript(ScriptBuilder.wrapReturnValue() .target(new MethodTarget(PARSER_SIG, "endParse", CUD_SIG, "int")) - .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform_swapped", "void", CUD_SIG, PARSER_SIG)) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform_swapped", "void", OBJECT_SIG, OBJECT_SIG)) .request(StackRequest.THIS, StackRequest.RETURN_VALUE).build()); } - private static void patchEcjTransformers(ScriptManager sm, boolean ecj) { - addPatchesForDelegate(sm, ecj); + private static void patchEcjTransformers(ScriptManager sm) { + addPatchesForDelegate(sm); addPatchesForVal(sm); - if (!ecj) addPatchesForValEclipse(sm); + addPatchesForValEclipse(sm); } - private static void addPatchesForDelegate(ScriptManager sm, boolean ecj) { + private static void addPatchesForDelegate(ScriptManager sm) { final String CLASSSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.ClassScope"; sm.addScript(ScriptBuilder.exitEarly() @@ -655,6 +696,26 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .request(StackRequest.THIS) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Delegate", "handleDelegateForType", "boolean", "java.lang.Object")) .build()); + + sm.addScriptIfWitness(OSGI_TYPES, 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.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() + .fieldName("$delegateMethods") + .fieldType("Ljava/util/Map;") + .setPublic() + .setTransient() + .targetClass("org.eclipse.jdt.internal.core.CompilationUnit") + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, 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", "addGeneratedDelegateMethods", "java.lang.Object[]", "java.lang.Object", "java.lang.Object")) + .build()); } private static void addPatchesForValEclipse(ScriptManager sm) { @@ -664,7 +725,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String SINGLEVARIABLEDECLARATION_SIG = "org.eclipse.jdt.core.dom.SingleVariableDeclaration"; final String ASTCONVERTER_SIG = "org.eclipse.jdt.core.dom.ASTConverter"; - sm.addScript(ScriptBuilder.addField() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() .fieldName("$initCopy") .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;") .setPublic() @@ -672,7 +733,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") .build()); - sm.addScript(ScriptBuilder.addField() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() .fieldName("$iterableCopy") .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;") .setPublic() @@ -680,25 +741,25 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") .build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget(PARSER_SIG, "consumeExitVariableWithInitialization", "void")) .request(StackRequest.THIS) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "copyInitializationOfLocalDeclaration", "void", "java.lang.Object")) .build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget(PARSER_SIG, "consumeEnhancedForStatementHeader", "void")) .request(StackRequest.THIS) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "copyInitializationOfForEachIterable", "void", "java.lang.Object")) .build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget(ASTCONVERTER_SIG, "setModifiers", "void", VARIABLEDECLARATIONSTATEMENT_SIG, LOCALDECLARATION_SIG)) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "addFinalAndValAnnotationToVariableDeclarationStatement", "void", "java.lang.Object", "java.lang.Object", "java.lang.Object")) .request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget(ASTCONVERTER_SIG, "setModifiers", "void", SINGLEVARIABLEDECLARATION_SIG, LOCALDECLARATION_SIG)) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "addFinalAndValAnnotationToSingleVariableDeclaration", "void", "java.lang.Object", "java.lang.Object", "java.lang.Object")) @@ -711,11 +772,12 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String EXPRESSION_SIG = "org.eclipse.jdt.internal.compiler.ast.Expression"; final String BLOCKSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope"; final String TYPEBINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding"; + final String OBJECT_SIG = "java.lang.Object"; sm.addScript(ScriptBuilder.exitEarly() .target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG)) .request(StackRequest.THIS, StackRequest.PARAM1) - .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForLocalDeclaration", "boolean", LOCALDECLARATION_SIG, BLOCKSCOPE_SIG)) + .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForLocalDeclaration", "boolean", OBJECT_SIG, OBJECT_SIG)) .build()); sm.addScript(ScriptBuilder.replaceMethodCall() @@ -723,18 +785,20 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG)) .requestExtra(StackRequest.THIS) .replacementMethod(new Hook("lombok.launch.PatchFixesHider$Val", "skipResolveInitializerIfAlreadyCalled2", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG, LOCALDECLARATION_SIG)) + .transplant() .build()); sm.addScript(ScriptBuilder.replaceMethodCall() .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG)) .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG)) .replacementMethod(new Hook("lombok.launch.PatchFixesHider$Val", "skipResolveInitializerIfAlreadyCalled", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG)) + .transplant() .build()); sm.addScript(ScriptBuilder.exitEarly() .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG)) .request(StackRequest.THIS, StackRequest.PARAM1) - .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForForEach", "boolean", FOREACHSTATEMENT_SIG, BLOCKSCOPE_SIG)) + .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForForEach", "boolean", OBJECT_SIG, OBJECT_SIG)) .build()); } @@ -743,7 +807,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String I_ANNOTATABLE_SIG = "org.eclipse.jdt.core.IAnnotatable"; final String ANNOTATION_SIG = "org.eclipse.jdt.internal.compiler.ast.Annotation"; - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget(SOURCE_TYPE_CONVERTER_SIG, "convertAnnotations", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG)) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "convertAnnotations", ANNOTATION_SIG + "[]", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG)) .request(StackRequest.PARAM1, StackRequest.RETURN_VALUE).build()); @@ -753,7 +817,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String ASTNODE_SIG = "org.eclipse.jdt.core.dom.ASTNode"; final String PATCH_DEBUG = "lombok.eclipse.agent.PatchDiagnostics"; - sm.addScript(exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, exitEarly() .target(new MethodTarget(ASTNODE_SIG, "setSourceRange", "void", "int", "int")) .request(StackRequest.THIS) .request(StackRequest.PARAM1) @@ -762,7 +826,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .build()); } - private static void patchExtensionMethod(ScriptManager sm, boolean ecj) { + private static void patchExtensionMethod(ScriptManager sm) { final String PATCH_EXTENSIONMETHOD = "lombok.launch.PatchFixesHider$ExtensionMethod"; final String PATCH_EXTENSIONMETHOD_COMPLETIONPROPOSAL_PORTAL = "lombok.eclipse.agent.PatchExtensionMethodCompletionProposalPortal"; final String MESSAGE_SEND_SIG = "org.eclipse.jdt.internal.compiler.ast.MessageSend"; @@ -774,42 +838,50 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String METHOD_BINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.MethodBinding"; final String COMPLETION_PROPOSAL_COLLECTOR_SIG = "org.eclipse.jdt.ui.text.java.CompletionProposalCollector"; final String I_JAVA_COMPLETION_PROPOSAL_SIG = "org.eclipse.jdt.ui.text.java.IJavaCompletionProposal[]"; + final String AST_NODE = "org.eclipse.jdt.internal.compiler.ast.ASTNode"; + final String OBJECT_SIG = "java.lang.Object"; sm.addScript(wrapReturnValue() .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) .request(StackRequest.RETURN_VALUE) .request(StackRequest.THIS) .request(StackRequest.PARAM1) - .wrapMethod(new Hook(PATCH_EXTENSIONMETHOD, "resolveType", TYPE_BINDING_SIG, TYPE_BINDING_SIG, MESSAGE_SEND_SIG, BLOCK_SCOPE_SIG)) + .wrapMethod(new Hook(PATCH_EXTENSIONMETHOD, "resolveType", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) + .cast() .build()); sm.addScript(replaceMethodCall() .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "errorNoMethodFor", "void", MESSAGE_SEND_SIG, TYPE_BINDING_SIG, TYPE_BINDINGS_SIG)) - .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "errorNoMethodFor", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, TYPE_BINDING_SIG, TYPE_BINDINGS_SIG)) + .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "errorNoMethodFor", "void", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) .build()); sm.addScript(replaceMethodCall() .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "invalidMethod", "void", MESSAGE_SEND_SIG, METHOD_BINDING_SIG)) - .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG)) + .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) .build()); // Since eclipse mars; they added a param. sm.addScript(replaceMethodCall() .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "invalidMethod", "void", MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG)) - .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG)) + .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) .build()); - if (!ecj) { - sm.addScript(wrapReturnValue() - .target(new MethodTarget(COMPLETION_PROPOSAL_COLLECTOR_SIG, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG)) - .request(StackRequest.RETURN_VALUE) - .request(StackRequest.THIS) - .wrapMethod(new Hook(PATCH_EXTENSIONMETHOD_COMPLETIONPROPOSAL_PORTAL, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG, "java.lang.Object[]", "java.lang.Object")) - .build()); - } + sm.addScript(replaceMethodCall() + .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) + .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "nonStaticAccessToStaticMethod", "void", AST_NODE, METHOD_BINDING_SIG)) + .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "nonStaticAccessToStaticMethod", "void", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) + .requestExtra(StackRequest.THIS) + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, wrapReturnValue() + .target(new MethodTarget(COMPLETION_PROPOSAL_COLLECTOR_SIG, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG)) + .request(StackRequest.RETURN_VALUE) + .request(StackRequest.THIS) + .wrapMethod(new Hook(PATCH_EXTENSIONMETHOD_COMPLETIONPROPOSAL_PORTAL, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG, "java.lang.Object[]", "java.lang.Object")) + .build()); } private static void patchNullCheck(ScriptManager sm) { @@ -830,5 +902,37 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .request(StackRequest.PARAM1) .transplant().build()); } + + private static void patchJavadoc(ScriptManager sm) { + sm.addScriptIfWitness(OSGI_TYPES, 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.addScriptIfWitness(OSGI_TYPES, 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.addScriptIfWitness(OSGI_TYPES, 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..d7b17598 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-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,8 +22,8 @@ package lombok.eclipse.agent; import static lombok.eclipse.Eclipse.*; +import static lombok.eclipse.EcjAugments.*; 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,28 @@ 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; +import lombok.permit.Permit; public class PatchDelegate { + private static class ClassScopeEntry { ClassScopeEntry(ClassScope scope) { this.scope = scope; @@ -123,6 +145,14 @@ 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) { + if (eclipseAvailable) { + EclipseOnlyMethods.cleanupDelegateMethods(cud); + } + } + if (!hasDelegateMarkedFieldsOrMethods(scope.referenceContext)) return false; List<ClassScopeEntry> stack = visited.get(); @@ -149,7 +179,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,6 +197,9 @@ public class PatchDelegate { } } finally { stack.remove(stack.size() - 1); + if (stack.isEmpty() && eclipseAvailable) { + EclipseOnlyMethods.notifyDelegateMethodsAdded(cud); + } } } @@ -419,6 +451,7 @@ public class PatchDelegate { private static void generateDelegateMethods(EclipseNode typeNode, List<BindingTuple> methods, DelegateReceiver delegateReceiver) { CompilationUnitDeclaration top = (CompilationUnitDeclaration) typeNode.top().get(); + List<MethodDeclaration> addedMethods = new ArrayList<MethodDeclaration>(); for (BindingTuple pair : methods) { EclipseNode annNode = typeNode.getAst().get(pair.responsible); MethodDeclaration method = createDelegateMethod(pair.fieldName, typeNode, pair, top.compilationResult, annNode, delegateReceiver); @@ -426,8 +459,12 @@ public class PatchDelegate { SetGeneratedByVisitor visitor = new SetGeneratedByVisitor(annNode.get()); method.traverse(visitor, ((TypeDeclaration)typeNode.get()).scope); injectMethod(typeNode, method); + addedMethods.add(method); } } + if (eclipseAvailable) { + EclipseOnlyMethods.collectGeneratedDelegateMethods(top, typeNode, addedMethods); + } } public static void checkConflictOfTypeVarNames(BindingTuple binding, EclipseNode typeNode) throws CantMakeDelegates { @@ -673,20 +710,177 @@ public class PatchDelegate { return method; } + private static boolean eclipseAvailable = true; + static { + try { + CompilationUnit.class.getName(); + } catch (Throwable t) { + eclipseAvailable = false; + } + } + + public static Object[] addGeneratedDelegateMethods(Object[] returnValue, Object javaElement) { + if (Symbols.hasSymbol("lombok.skipdelegates")) return returnValue; + if (!eclipseAvailable) return returnValue; + + return EclipseOnlyMethods.addGeneratedDelegateMethodsToChildren(returnValue, javaElement); + } + + public static class EclipseOnlyMethods { + private static void cleanupDelegateMethods(CompilationUnitDeclaration cud) { + CompilationUnit compilationUnit = getCompilationUnit(cud); + if (compilationUnit != null) { + EclipseAugments.CompilationUnit_delegateMethods.clear(compilationUnit); + } + } + + public static void collectGeneratedDelegateMethods(CompilationUnitDeclaration top, EclipseNode typeNode, List<MethodDeclaration> addedMethods) { + String qualifiedName = new String(CharOperation.concatWith(getQualifiedInnerName(typeNode.up(), typeNode.getName().toCharArray()), '$')); + SourceType sourceType = getSourceType(top, qualifiedName); + List<SourceMethod> generatedMethods = getGeneratedMethods(sourceType); + if (generatedMethods == null) return; + + for (MethodDeclaration md : addedMethods) { + generatedMethods.add(DelegateSourceMethod.forMethodDeclaration(sourceType, md)); + } + } + + public static Object[] addGeneratedDelegateMethodsToChildren(Object[] returnValue, Object javaElement) { + List<SourceMethod> delegateMethods = getGeneratedMethods((SourceType) ((SourceTypeElementInfo) javaElement).getHandle()); + if (delegateMethods != null) { + return concat((IJavaElement[]) returnValue, delegateMethods.toArray(new IJavaElement[0]), IJavaElement.class); + } + return returnValue; + } + + private static void notifyDelegateMethodsAdded(CompilationUnitDeclaration cud) { + CompilationUnit compilationUnit = getCompilationUnit(cud); + if (compilationUnit != null) { + DeltaProcessor deltaProcessor = JavaModelManager.getJavaModelManager().getDeltaProcessor(); + deltaProcessor.fire(new JavaElementDelta(compilationUnit), ElementChangedEvent.POST_CHANGE); + } + } + + 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> getGeneratedMethods(SourceType sourceType) { + if (sourceType != null) { + CompilationUnit compilationUnit = getCompilationUnit(sourceType.getCompilationUnit()); + if (compilationUnit != null) { + ConcurrentMap<String, List<SourceMethod>> map = EclipseAugments.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; - + public static final Throwable initCause; static { Method m = null; + Throwable c = null; try { - m = ClassScope.class.getDeclaredMethod("buildFieldsAndMethods"); - m.setAccessible(true); + m = Permit.getMethod(ClassScope.class, "buildFieldsAndMethods"); } catch (Throwable t) { + c = 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. } classScopeBuildFieldsAndMethodsMethod = m; + initCause = c; } } @@ -723,16 +917,14 @@ public class PatchDelegate { ClassScope cs = ((SourceTypeBinding)inner).scope; if (cs != null) { try { - Reflection.classScopeBuildFieldsAndMethodsMethod.invoke(cs); + Permit.invoke(Reflection.initCause, Reflection.classScopeBuildFieldsAndMethodsMethod, cs); } catch (Exception e) { // See 'Reflection' class for why we ignore this exception. } } } - if (!(binding instanceof ReferenceBinding)) { - return; - } + if (!(binding instanceof ReferenceBinding)) return; ReferenceBinding rb = (ReferenceBinding) binding; MethodBinding[] availableMethods = rb.availableMethods(); diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java index 49083df0..01e4bb18 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-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,52 +21,54 @@ */ package lombok.eclipse.agent; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import lombok.Lombok; +import lombok.permit.Permit; 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 { - return (Boolean) Reflection.handleDelegateForType.invoke(null, classScope); - } catch (NoClassDefFoundError e) { - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - return false; - } 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 false; - } + Boolean v = (Boolean) Permit.invokeSneaky(Reflection.problemHandleDelegate, Reflection.handleDelegateForType, null, classScope); + if (v == null) return false; + return v.booleanValue(); + } + + public static Object[] addGeneratedDelegateMethods(Object returnValue, Object javaElement) { + return (Object[]) Permit.invokeSneaky(Reflection.problemAddGeneratedDelegateMethods, Reflection.addGeneratedDelegateMethods, null, returnValue, javaElement); } private static final class Reflection { public static final Method handleDelegateForType; - public static final Throwable problem; + public static final Method addGeneratedDelegateMethods; + public static final Throwable problemHandleDelegate; + public static final Throwable problemAddGeneratedDelegateMethods; static { - Method m = null; - Throwable problem_ = null; + Method m = null, n = null; + Throwable problemHandleDelegate_ = null; + Throwable problemAddGeneratedDelegateMethods_ = null; try { - m = PatchDelegate.class.getMethod("handleDelegateForType", Class.forName(CLASS_SCOPE)); + m = Permit.getMethod(PatchDelegate.class, "handleDelegateForType", Class.forName(CLASS_SCOPE)); } 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; + problemHandleDelegate_ = t; } handleDelegateForType = m; - problem = problem_; + problemHandleDelegate = problemHandleDelegate_; + + try { + n = Permit.getMethod(PatchDelegate.class, "addGeneratedDelegateMethods", Object[].class, Object.class); + } 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. + problemAddGeneratedDelegateMethods_ = t; + } + addGeneratedDelegateMethods = n; + problemAddGeneratedDelegateMethods = problemAddGeneratedDelegateMethods_; } } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java index fcc76059..2e540b5e 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -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; @@ -54,9 +55,9 @@ import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SuperReference; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; -import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; @@ -98,13 +99,13 @@ public class PatchExtensionMethod { private static final Method shortMethod = getMethod("invalidMethod", MessageSend.class, MethodBinding.class); private static final Method longMethod = getMethod("invalidMethod", MessageSend.class, MethodBinding.class, Scope.class); + private static Throwable initProblem; private static Method getMethod(String name, Class<?>... types) { try { - Method m = ProblemReporter.class.getMethod(name, types); - m.setAccessible(true); - return m; + return Permit.getMethod(ProblemReporter.class, name, types); } catch (Exception e) { + initProblem = e; return null; } } @@ -119,8 +120,9 @@ public class PatchExtensionMethod { static void invoke(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) { if (messageSend != null) { try { - if (shortMethod != null) shortMethod.invoke(problemReporter, messageSend, method); - else if (longMethod != null) longMethod.invoke(problemReporter, messageSend, method, scope); + if (shortMethod != null) Permit.invoke(initProblem, shortMethod, problemReporter, messageSend, method); + else if (longMethod != null) Permit.invoke(initProblem, longMethod, problemReporter, messageSend, method, scope); + else Permit.reportReflectionProblem(initProblem, "method named 'invalidMethod' not found in ProblemReporter.class"); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { @@ -138,6 +140,25 @@ public class PatchExtensionMethod { } } + private static class PostponedNonStaticAccessToStaticMethodError implements PostponedError { + private final ProblemReporter problemReporter; + private ASTNode location; + private MethodBinding method; + private ReferenceContext referenceContext; + + PostponedNonStaticAccessToStaticMethodError(ProblemReporter problemReporter, ASTNode location, MethodBinding method) { + this.problemReporter = problemReporter; + this.location = location; + this.method = method; + this.referenceContext = problemReporter.referenceContext; + } + + public void fire() { + problemReporter.referenceContext = this.referenceContext; + problemReporter.nonStaticAccessToStaticMethod(location, method); + } + } + private static interface PostponedError { public void fire(); } @@ -200,15 +221,12 @@ public class PatchExtensionMethod { TypeBinding receiverType) { List<MethodBinding> extensionMethods = new ArrayList<MethodBinding>(); - CompilationUnitScope cuScope = ((CompilationUnitDeclaration) typeNode.top().get()).scope; for (MethodBinding method : extensionMethodProviderBinding.methods()) { if (!method.isStatic()) continue; if (!method.isPublic()) continue; if (method.parameters == null || method.parameters.length == 0) continue; TypeBinding firstArgType = method.parameters[0]; if (receiverType.isProvablyDistinct(firstArgType) && !receiverType.isCompatibleWith(firstArgType.erasure())) continue; - TypeBinding[] argumentTypes = Arrays.copyOfRange(method.parameters, 1, method.parameters.length); - if ((receiverType instanceof ReferenceBinding) && ((ReferenceBinding) receiverType).getExactMethod(method.selector, argumentTypes, cuScope) != null) continue; extensionMethods.add(method); } return extensionMethods; @@ -228,6 +246,10 @@ public class PatchExtensionMethod { MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method, scope)); } + public static void nonStaticAccessToStaticMethod(ProblemReporter problemReporter, ASTNode location, MethodBinding method, MessageSend messageSend) { + MessageSend_postponedErrors.set(messageSend, new PostponedNonStaticAccessToStaticMethodError(problemReporter, location, method)); + } + public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) { List<Extension> extensions = new ArrayList<Extension>(); TypeDeclaration decl = scope.classScope().referenceContext; @@ -250,6 +272,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; @@ -262,13 +292,31 @@ 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]); + + if (methodCall.receiver instanceof MessageSend) { + if (Reflection.inferenceContexts != null) { + try { + Permit.set(Reflection.inferenceContexts, methodCall.receiver, null); + } catch (IllegalAccessException ignore) { + // ignore + } + } + } + MethodBinding fixedBinding = scope.getMethod(extensionMethod.declaringClass, methodCall.selector, argumentTypes.toArray(new TypeBinding[0]), methodCall); if (fixedBinding instanceof ProblemMethodBinding) { methodCall.arguments = originalArgs; @@ -276,18 +324,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 + } } } @@ -295,6 +358,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])); @@ -340,16 +404,42 @@ 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"); + public static final Field inferenceContexts = Permit.permissiveGetField(MessageSend.class, "inferenceContexts"); + 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..1b94d1b9 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; @@ -195,7 +198,7 @@ public class PatchExtensionMethodCompletionProposal { public static final Field completionEngineField; public static final Field nameLookupField; public static final Method createJavaCompletionProposalMethod; - + static { replacementOffsetField = accessField(AbstractJavaCompletionProposal.class, "fReplacementOffset"); contextField = accessField(CompletionProposalCollector.class, "fContext"); diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java deleted file mode 100644 index b66bafbb..00000000 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java +++ /dev/null @@ -1,103 +0,0 @@ -package lombok.eclipse.agent; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import lombok.Lombok; - -import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; - -public class PatchExtensionMethodPortal { - private static final String TYPE_BINDING = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding"; - private static final String TYPE_BINDING_ARRAY = "[Lorg.eclipse.jdt.internal.compiler.lookup.TypeBinding;"; - private static final String MESSAGE_SEND = "org.eclipse.jdt.internal.compiler.ast.MessageSend"; - private static final String BLOCK_SCOPE = "org.eclipse.jdt.internal.compiler.lookup.BlockScope"; - private static final String METHOD_BINDING = "org.eclipse.jdt.internal.compiler.lookup.MethodBinding"; - private static final String PROBLEM_REPORTER = "org.eclipse.jdt.internal.compiler.problem.ProblemReporter"; - - public static TypeBinding resolveType(Object resolvedType, Object methodCall, Object scope) { - try { - return (TypeBinding) Reflection.resolveType.invoke(null, resolvedType, methodCall, scope); - } catch (NoClassDefFoundError e) { - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - return (TypeBinding) resolvedType; - } catch (IllegalAccessException e) { - throw Lombok.sneakyThrow(e); - } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e); - } 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 (TypeBinding)resolvedType; - } - } - - public static void errorNoMethodFor(Object problemReporter, Object messageSend, Object recType, Object params) { - try { - Reflection.errorNoMethodFor.invoke(null, problemReporter, messageSend, recType, params); - } catch (NoClassDefFoundError e) { - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - } 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. - } - } - - public static void invalidMethod(Object problemReporter, Object messageSend, Object method) { - try { - Reflection.invalidMethod.invoke(null, problemReporter, messageSend, method); - } catch (NoClassDefFoundError e) { - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - } 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. - } - } - - private static final class Reflection { - public static final Method resolveType, errorNoMethodFor, invalidMethod; - public static final Throwable problem; - - static { - Method m = null, n = null, o = null; - Throwable problem_ = null; - try { - m = PatchExtensionMethod.class.getMethod("resolveType", Class.forName(TYPE_BINDING), Class.forName(MESSAGE_SEND), Class.forName(BLOCK_SCOPE)); - n = PatchExtensionMethod.class.getMethod("errorNoMethodFor", Class.forName(PROBLEM_REPORTER), - Class.forName(MESSAGE_SEND), Class.forName(TYPE_BINDING), Class.forName(TYPE_BINDING_ARRAY)); - o = PatchExtensionMethod.class.getMethod("invalidMethod", Class.forName(PROBLEM_REPORTER), Class.forName(MESSAGE_SEND), Class.forName(METHOD_BINDING)); - } 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; - } - resolveType = m; - errorNoMethodFor = n; - invalidMethod = o; - problem = problem_; - } - } -} diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java b/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java new file mode 100644 index 00000000..5b34917e --- /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.EcjAugments.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.handlers.EclipseHandlerUtil; +import lombok.permit.Permit; + +public class PatchJavadoc { + + public static String getHTMLContentFromSource(String original, Object 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 = CompilationUnit_javadoc.get(compilationUnit); + if (docs == null) return null; + + String signature = Signature.getSignature(sourceMethod); + String rawJavadoc = docs.get(signature); + if (rawJavadoc == null) return null; + + return Reflection.javadoc2HTML((IMember) member, (IJavaElement) member, rawJavadoc); + } + } + + return null; + } + + public static StringBuffer printMethod(AbstractMethodDeclaration methodDeclaration, Integer tab, StringBuffer output, TypeDeclaration type) { + Map<String, String> docs = CompilationUnit_javadoc.get(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 class Signature { + static final 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(org.eclipse.jdt.core.Signature.toString(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 f22e78a8..9663f364 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2019 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -68,32 +68,7 @@ public class PatchVal { // This is half of the work for 'val' support - the other half is in PatchValEclipse. This half is enough for ecj. // Creates a copy of the 'initialization' field on a LocalDeclaration if the type of the LocalDeclaration is 'val', because the completion parser will null this out, // which in turn stops us from inferring the intended type for 'val x = 5;'. We look at the copy. - // Also patches local declaration to not call .resolveType() on the initializer expression if we've already done so (calling it twice causes weird errors), - // and patches .resolve() on LocalDeclaration itself to just-in-time replace the 'val' vartype with the right one. - - public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) { - if (expr.resolvedType != null) return expr.resolvedType; - try { - return expr.resolveType(scope); - } catch (NullPointerException e) { - return null; - } catch (ArrayIndexOutOfBoundsException e) { - // This will occur internally due to for example 'val x = mth("X");', where mth takes 2 arguments. - return null; - } - } - - public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) { - if (decl != null && LocalDeclaration.class.equals(decl.getClass()) && expr.resolvedType != null) return expr.resolvedType; - try { - return expr.resolveType(scope); - } catch (NullPointerException e) { - return null; - } catch (ArrayIndexOutOfBoundsException e) { - // This will occur internally due to for example 'val x = mth("X");', where mth takes 2 arguments. - return null; - } - } + // Also patches .resolve() on LocalDeclaration itself to just-in-time replace the 'val' vartype with the right one. public static boolean matches(String key, char[] array) { if (array == null || key.length() != array.length) return false; diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java index deab0be1..02df3f5f 100755 --- a/src/eclipseAgent/lombok/launch/PatchFixesHider.java +++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2019 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,27 +35,22 @@ 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.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; 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; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; -import org.eclipse.jdt.internal.compiler.parser.Parser; -import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.core.SourceField; import org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent; import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent; @@ -63,7 +58,7 @@ import org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner; import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup; import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil; -import lombok.eclipse.EclipseAugments; +import static lombok.eclipse.EcjAugments.ASTNode_generatedBy; /** These contain a mix of the following: * <ul> @@ -84,20 +79,24 @@ final class PatchFixesHider { public static final class Util { private static ClassLoader shadowLoader; + public static ClassLoader getShadowLoader() { + if (shadowLoader == null) { + try { + Class.forName("lombok.core.LombokNode"); + // If we get here, then lombok is already available. + shadowLoader = Util.class.getClassLoader(); + } catch (ClassNotFoundException e) { + // If we get here, it isn't, and we should use the shadowloader. + shadowLoader = Main.getShadowClassLoader(); + } + } + + return shadowLoader; + } + public static Class<?> shadowLoadClass(String name) { try { - if (shadowLoader == null) { - try { - Class.forName("lombok.core.LombokNode"); - // If we get here, then lombok is already available. - shadowLoader = Util.class.getClassLoader(); - } catch (ClassNotFoundException e) { - // If we get here, it isn't, and we should use the shadowloader. - shadowLoader = Main.getShadowClassLoader(); - } - } - - return Class.forName(name, true, shadowLoader); + return Class.forName(name, true, getShadowLoader()); } catch (ClassNotFoundException e) { throw sneakyThrow(e); } @@ -111,6 +110,20 @@ final class PatchFixesHider { } } + public static Method findMethod(Class<?> type, String name, String... parameterTypes) { + for (Method m : type.getDeclaredMethods()) { + if (name.equals(m.getName()) && sameTypes(m.getParameterTypes(), parameterTypes)) { + return m; + } + } + throw sneakyThrow(new NoSuchMethodException(type.getName() + "::" + name)); + } + + public static Method findMethodAnyArgs(Class<?> type, String name) { + for (Method m : type.getDeclaredMethods()) if (name.equals(m.getName())) return m; + throw sneakyThrow(new NoSuchMethodException(type.getName() + "::" + name)); + } + public static Object invokeMethod(Method method, Object... args) { try { return method.invoke(null, args); @@ -131,6 +144,14 @@ final class PatchFixesHider { private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T { throw (T)t; } + + private static boolean sameTypes(Class<?>[] types, String[] typeNames) { + if (types.length != typeNames.length) return false; + for (int i = 0; i < types.length; i++) { + if (!types[i].getName().equals(typeNames[i])) return false; + } + return true; + } } /** Contains patch fixes that are dependent on lombok internals. */ @@ -170,20 +191,25 @@ final class PatchFixesHider { } public static final class Transform { - private static final Method TRANSFORM; - private static final Method TRANSFORM_SWAPPED; + private static Method TRANSFORM; + private static Method TRANSFORM_SWAPPED; - static { + private static synchronized void init(ClassLoader prepend) { + if (TRANSFORM != null) return; + + Main.prependClassLoader(prepend); Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.TransformEclipseAST"); - TRANSFORM = Util.findMethod(shadowed, "transform", Parser.class, CompilationUnitDeclaration.class); - TRANSFORM_SWAPPED = Util.findMethod(shadowed, "transform_swapped", CompilationUnitDeclaration.class, Parser.class); + TRANSFORM = Util.findMethodAnyArgs(shadowed, "transform"); + TRANSFORM_SWAPPED = Util.findMethodAnyArgs(shadowed, "transform_swapped"); } - public static void transform(Parser parser, CompilationUnitDeclaration ast) throws IOException { + public static void transform(Object parser, Object ast) throws IOException { + init(parser.getClass().getClassLoader()); Util.invokeMethod(TRANSFORM, parser, ast); } - public static void transform_swapped(CompilationUnitDeclaration ast, Parser parser) throws IOException { + public static void transform_swapped(Object ast, Object parser) throws IOException { + init(parser.getClass().getClassLoader()); Util.invokeMethod(TRANSFORM_SWAPPED, ast, parser); } } @@ -191,15 +217,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 ADD_GENERATED_DELEGATE_METHODS; static { Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchDelegatePortal"); HANDLE_DELEGATE_FOR_TYPE = Util.findMethod(shadowed, "handleDelegateForType", Object.class); + ADD_GENERATED_DELEGATE_METHODS = Util.findMethod(shadowed, "addGeneratedDelegateMethods", Object.class, Object.class); } public static boolean handleDelegateForType(Object classScope) { return (Boolean) Util.invokeMethod(HANDLE_DELEGATE_FOR_TYPE, classScope); } + + public static Object[] addGeneratedDelegateMethods(Object returnValue, Object javaElement) { + return (Object[]) Util.invokeMethod(ADD_GENERATED_DELEGATE_METHODS, returnValue, javaElement); + } } /** Contains patch code to support {@code val} (eclipse specific) */ @@ -236,65 +268,120 @@ final class PatchFixesHider { /** Contains patch code to support {@code val} (eclipse and ecj) */ public static final class Val { - private static final Method SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED; - private static final Method SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2; + private static final String BLOCK_SCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope"; + private static final String LOCAL_DECLARATION_SIG = "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration"; + private static final String FOREACH_STATEMENT_SIG = "org.eclipse.jdt.internal.compiler.ast.ForeachStatement"; + private static final Method HANDLE_VAL_FOR_LOCAL_DECLARATION; private static final Method HANDLE_VAL_FOR_FOR_EACH; static { Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchVal"); - SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED = Util.findMethod(shadowed, "skipResolveInitializerIfAlreadyCalled", Expression.class, BlockScope.class); - SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2 = Util.findMethod(shadowed, "skipResolveInitializerIfAlreadyCalled2", Expression.class, BlockScope.class, LocalDeclaration.class); - HANDLE_VAL_FOR_LOCAL_DECLARATION = Util.findMethod(shadowed, "handleValForLocalDeclaration", LocalDeclaration.class, BlockScope.class); - HANDLE_VAL_FOR_FOR_EACH = Util.findMethod(shadowed, "handleValForForEach", ForeachStatement.class, BlockScope.class); + HANDLE_VAL_FOR_LOCAL_DECLARATION = Util.findMethod(shadowed, "handleValForLocalDeclaration", LOCAL_DECLARATION_SIG, BLOCK_SCOPE_SIG); + HANDLE_VAL_FOR_FOR_EACH = Util.findMethod(shadowed, "handleValForForEach", FOREACH_STATEMENT_SIG, BLOCK_SCOPE_SIG); } - public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) { - return (TypeBinding) Util.invokeMethod(SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED, expr, scope); + public static boolean handleValForLocalDeclaration(Object local, Object scope) { + return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_LOCAL_DECLARATION, local, scope); } - public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) { - return (TypeBinding) Util.invokeMethod(SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2, expr, scope, decl); + public static boolean handleValForForEach(Object forEach, Object scope) { + return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_FOR_EACH, forEach, scope); } - public static boolean handleValForLocalDeclaration(LocalDeclaration local, BlockScope scope) { - return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_LOCAL_DECLARATION, local, scope); + /** + * Patches local declaration to not call .resolveType() on the initializer expression if we've already done so (calling it twice causes weird errors) + * This and the next method must be transplanted so that the return type is loaded in the correct class loader + */ + public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) { + if (expr.resolvedType != null) return expr.resolvedType; + try { + return expr.resolveType(scope); + } catch (NullPointerException e) { + return null; + } catch (ArrayIndexOutOfBoundsException e) { + // This will occur internally due to for example 'val x = mth("X");', where mth takes 2 arguments. + return null; + } } - public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) { - return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_FOR_EACH, forEach, scope); + public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) { + if (decl != null && LocalDeclaration.class.equals(decl.getClass()) && expr.resolvedType != null) return expr.resolvedType; + try { + return expr.resolveType(scope); + } catch (NullPointerException e) { + return null; + } catch (ArrayIndexOutOfBoundsException e) { + // This will occur internally due to for example 'val x = mth("X");', where mth takes 2 arguments. + return null; + } } } /** Contains patch code to support {@code @ExtensionMethod} */ public static final class ExtensionMethod { + private static final String MESSAGE_SEND_SIG = "org.eclipse.jdt.internal.compiler.ast.MessageSend"; + private static final String TYPE_BINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding"; + private static final String SCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.Scope"; + private static final String BLOCK_SCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope"; + private static final String TYPE_BINDINGS_SIG = "[Lorg.eclipse.jdt.internal.compiler.lookup.TypeBinding;"; + private static final String PROBLEM_REPORTER_SIG = "org.eclipse.jdt.internal.compiler.problem.ProblemReporter"; + private static final String METHOD_BINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.MethodBinding"; + private static final String AST_NODE_SIG = "org.eclipse.jdt.internal.compiler.ast.ASTNode"; + private static final Method RESOLVE_TYPE; private static final Method ERROR_NO_METHOD_FOR; private static final Method INVALID_METHOD, INVALID_METHOD2; + private static final Method NON_STATIC_ACCESS_TO_STATIC_METHOD; static { Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchExtensionMethod"); - RESOLVE_TYPE = Util.findMethod(shadowed, "resolveType", TypeBinding.class, MessageSend.class, BlockScope.class); - ERROR_NO_METHOD_FOR = Util.findMethod(shadowed, "errorNoMethodFor", ProblemReporter.class, MessageSend.class, TypeBinding.class, TypeBinding[].class); - INVALID_METHOD = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class); - INVALID_METHOD2 = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class, Scope.class); + RESOLVE_TYPE = Util.findMethod(shadowed, "resolveType", TYPE_BINDING_SIG, MESSAGE_SEND_SIG, BLOCK_SCOPE_SIG); + ERROR_NO_METHOD_FOR = Util.findMethod(shadowed, "errorNoMethodFor", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, TYPE_BINDING_SIG, TYPE_BINDINGS_SIG); + INVALID_METHOD = Util.findMethod(shadowed, "invalidMethod", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG); + INVALID_METHOD2 = Util.findMethod(shadowed, "invalidMethod", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG); + NON_STATIC_ACCESS_TO_STATIC_METHOD = Util.findMethod(shadowed, "nonStaticAccessToStaticMethod", PROBLEM_REPORTER_SIG, AST_NODE_SIG, METHOD_BINDING_SIG, MESSAGE_SEND_SIG); } - public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) { - return (TypeBinding) Util.invokeMethod(RESOLVE_TYPE, resolvedType, methodCall, scope); + public static Object resolveType(Object resolvedType, Object methodCall, Object scope) { + return Util.invokeMethod(RESOLVE_TYPE, resolvedType, methodCall, scope); } - public static void errorNoMethodFor(ProblemReporter problemReporter, MessageSend messageSend, TypeBinding recType, TypeBinding[] params) { + public static void errorNoMethodFor(Object problemReporter, Object messageSend, Object recType, Object params) { Util.invokeMethod(ERROR_NO_METHOD_FOR, problemReporter, messageSend, recType, params); } - public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) { + public static void invalidMethod(Object problemReporter, Object messageSend, Object method) { Util.invokeMethod(INVALID_METHOD, problemReporter, messageSend, method); } - public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) { + public static void invalidMethod(Object problemReporter, Object messageSend, Object method, Object scope) { Util.invokeMethod(INVALID_METHOD2, problemReporter, messageSend, method, scope); } + + public static void nonStaticAccessToStaticMethod(Object problemReporter, Object location, Object method, Object messageSend) { + Util.invokeMethod(NON_STATIC_ACCESS_TO_STATIC_METHOD, problemReporter, location, method, messageSend); + } + } + + /** 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, Object.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); + } } /** @@ -497,6 +584,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(); @@ -506,21 +597,21 @@ final class PatchFixesHider { public static int fixRetrieveRightBraceOrSemiColonPosition(int retVal, AbstractMethodDeclaration amd) { if (retVal != -1 || amd == null) return retVal; - boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(amd) != null; + boolean isGenerated = ASTNode_generatedBy.get(amd) != null; if (isGenerated) return amd.declarationSourceEnd; return -1; } public static int fixRetrieveRightBraceOrSemiColonPosition(int retVal, FieldDeclaration fd) { if (retVal != -1 || fd == null) return retVal; - boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(fd) != null; + boolean isGenerated = ASTNode_generatedBy.get(fd) != null; if (isGenerated) return fd.declarationSourceEnd; 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; } @@ -539,13 +630,13 @@ final class PatchFixesHider { org.eclipse.jdt.internal.compiler.ast.ASTNode internalNode) throws Exception { if (internalNode == null || domNode == null) return; - boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(internalNode) != null; + boolean isGenerated = ASTNode_generatedBy.get(internalNode) != null; if (isGenerated) domNode.getClass().getField("$isGenerated").set(domNode, true); } public static void setIsGeneratedFlagForName(org.eclipse.jdt.core.dom.Name name, Object internalNode) throws Exception { if (internalNode instanceof org.eclipse.jdt.internal.compiler.ast.ASTNode) { - boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get((org.eclipse.jdt.internal.compiler.ast.ASTNode) internalNode) != null; + boolean isGenerated = ASTNode_generatedBy.get((org.eclipse.jdt.internal.compiler.ast.ASTNode) internalNode) != null; if (isGenerated) name.getClass().getField("$isGenerated").set(name, true); } } diff --git a/src/installer/lombok/installer/Installer.java b/src/installer/lombok/installer/Installer.java index 94cc1a45..1ca77065 100644 --- a/src/installer/lombok/installer/Installer.java +++ b/src/installer/lombok/installer/Installer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2016 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -40,8 +40,7 @@ import lombok.core.SpiLoadUtil; import lombok.core.Version; import lombok.installer.OsUtils.OS; import lombok.patcher.ClassRootFinder; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.zwitserloot.cmdreader.CmdReader; import com.zwitserloot.cmdreader.Description; @@ -105,7 +104,7 @@ public class Installer { return a.equals(b); } - @ProviderFor(LombokApp.class) + @Provides public static class GraphicalInstallerApp extends LombokApp { @Override public String getAppName() { return "installer"; @@ -124,7 +123,7 @@ public class Installer { } } - @ProviderFor(LombokApp.class) + @Provides public static class CommandLineInstallerApp extends LombokApp { @Override public String getAppName() { return "install"; @@ -139,7 +138,7 @@ public class Installer { } } - @ProviderFor(LombokApp.class) + @Provides public static class CommandLineUninstallerApp extends LombokApp { @Override public String getAppName() { return "uninstall"; diff --git a/src/installer/lombok/installer/eclipse/AngularIDELocationProvider.java b/src/installer/lombok/installer/eclipse/AngularIDELocationProvider.java index 6d580e13..0f0aac0d 100644 --- a/src/installer/lombok/installer/eclipse/AngularIDELocationProvider.java +++ b/src/installer/lombok/installer/eclipse/AngularIDELocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Project Lombok Authors. + * Copyright (C) 2018-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,11 +23,10 @@ package lombok.installer.eclipse; import java.util.Collections; -import org.mangosdk.spi.ProviderFor; - import lombok.installer.IdeLocationProvider; +import lombok.spi.Provides; -@ProviderFor(IdeLocationProvider.class) +@Provides(IdeLocationProvider.class) public class AngularIDELocationProvider extends EclipseProductLocationProvider { private static final EclipseProductDescriptor ANGULAR = new StandardProductDescriptor( diff --git a/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java b/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java index fa2ce958..317c95cb 100644 --- a/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java +++ b/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2016 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,10 +24,9 @@ package lombok.installer.eclipse; import java.util.Collections; import lombok.installer.IdeLocationProvider; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(IdeLocationProvider.class) +@Provides(IdeLocationProvider.class) public class EclipseLocationProvider extends EclipseProductLocationProvider { private static final EclipseProductDescriptor ECLIPSE = new StandardProductDescriptor( diff --git a/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java b/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java index 635f304a..75805f21 100644 --- a/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java +++ b/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2016 The Project Lombok Authors. + * Copyright (C) 2013-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,10 +24,9 @@ package lombok.installer.eclipse; import java.util.Collections; import lombok.installer.IdeLocationProvider; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(IdeLocationProvider.class) +@Provides(IdeLocationProvider.class) public class JbdsLocationProvider extends EclipseProductLocationProvider { private static final EclipseProductDescriptor JBDS = new StandardProductDescriptor( diff --git a/src/installer/lombok/installer/eclipse/MyEclipseLocationProvider.java b/src/installer/lombok/installer/eclipse/MyEclipseLocationProvider.java index 298cabd6..e923202d 100644 --- a/src/installer/lombok/installer/eclipse/MyEclipseLocationProvider.java +++ b/src/installer/lombok/installer/eclipse/MyEclipseLocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Project Lombok Authors. + * Copyright (C) 2016-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,10 +24,9 @@ package lombok.installer.eclipse; import java.util.Collections; import lombok.installer.IdeLocationProvider; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(IdeLocationProvider.class) +@Provides(IdeLocationProvider.class) public class MyEclipseLocationProvider extends EclipseProductLocationProvider { private static final EclipseProductDescriptor MY_ECLIPSE = new StandardProductDescriptor( diff --git a/src/installer/lombok/installer/eclipse/RhcrLocationProvider.java b/src/installer/lombok/installer/eclipse/RhcrLocationProvider.java index d4f39f3b..0731d5bd 100644 --- a/src/installer/lombok/installer/eclipse/RhcrLocationProvider.java +++ b/src/installer/lombok/installer/eclipse/RhcrLocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Project Lombok Authors. + * Copyright (C) 2020-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,10 +24,9 @@ package lombok.installer.eclipse; import java.util.Collections; import lombok.installer.IdeLocationProvider; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(IdeLocationProvider.class) +@Provides(IdeLocationProvider.class) public class RhcrLocationProvider extends EclipseProductLocationProvider { private static final EclipseProductDescriptor RHCR = new StandardProductDescriptor( "Red Hat CodeReady Studio", diff --git a/src/installer/lombok/installer/eclipse/RhdsLocationProvider.java b/src/installer/lombok/installer/eclipse/RhdsLocationProvider.java index 5e1d303d..030b96bb 100644 --- a/src/installer/lombok/installer/eclipse/RhdsLocationProvider.java +++ b/src/installer/lombok/installer/eclipse/RhdsLocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2016 The Project Lombok Authors. + * Copyright (C) 2013-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,10 +24,9 @@ package lombok.installer.eclipse; import java.util.Collections; import lombok.installer.IdeLocationProvider; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(IdeLocationProvider.class) +@Provides(IdeLocationProvider.class) public class RhdsLocationProvider extends EclipseProductLocationProvider { private static final EclipseProductDescriptor RHDS = new StandardProductDescriptor( diff --git a/src/installer/lombok/installer/eclipse/STS4LocationProvider.java b/src/installer/lombok/installer/eclipse/STS4LocationProvider.java index 47a07bdd..068ec6e9 100644 --- a/src/installer/lombok/installer/eclipse/STS4LocationProvider.java +++ b/src/installer/lombok/installer/eclipse/STS4LocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Project Lombok Authors. + * Copyright (C) 2018-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,10 +25,9 @@ import java.util.Arrays; import java.util.Collections; import lombok.installer.IdeLocationProvider; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(IdeLocationProvider.class) +@Provides(IdeLocationProvider.class) public class STS4LocationProvider extends EclipseProductLocationProvider { private static final EclipseProductDescriptor STS4 = new StandardProductDescriptor("Spring Tools Suite 4", diff --git a/src/installer/lombok/installer/eclipse/STSLocationProvider.java b/src/installer/lombok/installer/eclipse/STSLocationProvider.java index d2efb956..7c42cf62 100644 --- a/src/installer/lombok/installer/eclipse/STSLocationProvider.java +++ b/src/installer/lombok/installer/eclipse/STSLocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2016 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,10 +24,9 @@ package lombok.installer.eclipse; import java.util.Collections; import lombok.installer.IdeLocationProvider; +import lombok.spi.Provides; -import org.mangosdk.spi.ProviderFor; - -@ProviderFor(IdeLocationProvider.class) +@Provides(IdeLocationProvider.class) public class STSLocationProvider extends EclipseProductLocationProvider { private static final EclipseProductDescriptor STS = new StandardProductDescriptor("STS", diff --git a/src/launch/lombok/launch/Main.java b/src/launch/lombok/launch/Main.java index 08298cc2..af65bdf8 100644 --- a/src/launch/lombok/launch/Main.java +++ b/src/launch/lombok/launch/Main.java @@ -34,6 +34,11 @@ class Main { return classLoader; } + static synchronized void prependClassLoader(ClassLoader loader) { + getShadowClassLoader(); + classLoader.prependParent(loader); + } + public static void main(String[] args) throws Throwable { ClassLoader cl = getShadowClassLoader(); Class<?> mc = cl.loadClass("lombok.core.Main"); diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java index da377ae4..929681ce 100644 --- a/src/launch/lombok/launch/ShadowClassLoader.java +++ b/src/launch/lombok/launch/ShadowClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2018 The Project Lombok Authors. + * Copyright (C) 2014-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,6 +38,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -102,6 +103,14 @@ class ShadowClassLoader extends ClassLoader { private final List<String> parentExclusion = new ArrayList<String>(); private final List<String> highlanders = new ArrayList<String>(); + private final Set<ClassLoader> prependedParentLoaders = Collections.newSetFromMap(new IdentityHashMap<ClassLoader, Boolean>()); + + public void prependParent(ClassLoader loader) { + if (loader == null) return; + if (loader == getParent()) return; + prependedParentLoaders.add(loader); + } + /** * @param source The 'parent' classloader. * @param sclSuffix The suffix of the shadowed class files in our own jar. For example, if this is {@code lombok}, then the class files in your jar should be {@code foo/Bar.SCL.lombok} and not {@code foo/Bar.class}. @@ -531,15 +540,31 @@ class ShadowClassLoader extends ClassLoader { String fileNameOfClass = name.replace(".", "/") + ".class"; URL res = getResource_(fileNameOfClass, true); if (res == null) { - if (!exclusionListMatch(fileNameOfClass)) try { - return super.loadClass(name, resolve); - } catch (ClassNotFoundException cnfe) { - res = getResource_("secondaryLoading.SCL." + sclSuffix + "/" + name.replace(".", "/") + ".SCL." + sclSuffix, true); - if (res == null) throw cnfe; + if (!exclusionListMatch(fileNameOfClass)) { + try { + // First search in the prepended classloaders, the class might be their already + for (ClassLoader pre : prependedParentLoaders) { + try { + Class<?> loadClass = pre.loadClass(name); + if (loadClass != null) return loadClass; + } catch (Throwable e) { + continue; + } + } + + return super.loadClass(name, resolve); + } catch (ClassNotFoundException cnfe) { + res = getResource_("secondaryLoading.SCL." + sclSuffix + "/" + name.replace(".", "/") + ".SCL." + sclSuffix, true); + if (res == null) throw cnfe; + } } } if (res == null) throw new ClassNotFoundException(name); + return urlToDefineClass(name, res, resolve); + } + + private Class<?> urlToDefineClass(String name, URL res, boolean resolve) throws ClassNotFoundException { byte[] b; int p = 0; try { diff --git a/src/spiProcessor/lombok/spi/Provides.java b/src/spiProcessor/lombok/spi/Provides.java new file mode 100644 index 00000000..de930819 --- /dev/null +++ b/src/spiProcessor/lombok/spi/Provides.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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.spi; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface Provides { + Class<?>[] value() default {}; +} diff --git a/src/spiProcessor/lombok/spi/SpiProcessor.java b/src/spiProcessor/lombok/spi/SpiProcessor.java new file mode 100644 index 00000000..4ad27bbe --- /dev/null +++ b/src/spiProcessor/lombok/spi/SpiProcessor.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2021 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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.spi; + +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; + +import javax.tools.Diagnostic.Kind; + +@SupportedAnnotationTypes("*") +public class SpiProcessor extends AbstractProcessor { + private SpiProcessorCollector data; + private SpiProcessorPersistence persistence; + + static String getRootPathOfServiceFiles() { + return "META-INF/services/"; + } + + @Override public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + static String toErrorMsg(Exception e, String pathName) { + StringBuilder sb = new StringBuilder(); + sb.append("Exception applying SPI processor on ").append(pathName).append(": ").append(toStringLong(e)); + return sb.toString(); + } + + private static String toStringLong(Throwable t) { + if (t == null) return "NULL"; + StringBuilder out = new StringBuilder(); + + String msg = t.getMessage(); + out.append(t.getClass().getName()); + if (msg != null) out.append(": ").append(msg); + String indent = " "; + + StackTraceElement[] elems = t.getStackTrace(); + if (elems != null) for (int i = 0; i < elems.length; i++) { + out.append("\n").append(indent).append(elems[i]); + } + Throwable cause = t.getCause(); + while (cause != null) { + indent = indent + " "; + out.append("\n").append(indent).append("Caused by: ").append(cause.getClass().getName()); + msg = cause.getMessage(); + if (msg != null) out.append(": ").append(msg); + elems = cause.getStackTrace(); + indent = indent + " "; + if (elems != null) for (int i = 0; i < elems.length; i++) { + out.append("\n").append(indent).append(elems[i]); + } + cause = cause.getCause(); + } + return out.toString(); + } + + @Override public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + persistence = new SpiProcessorPersistence("SpiProcessor", processingEnv.getFiler(), processingEnv.getMessager()); + data = new SpiProcessorCollector(processingEnv); + for (String serviceName : persistence.tryFind()) data.getService(serviceName); + data.stripProvidersWithoutSourceFile(); + } + + @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { + removeStaleData(roundEnv); + handleAnnotations(roundEnv); + if (roundEnv.processingOver()) writeData(); + + return false; + } + + private void writeData() { + for (SpiProcessorService service : data.services()) { + try { + persistence.write(service.getName(), service.toProvidersListFormat()); + } + catch (IOException e) { + processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage()); + } + } + } + + private void removeStaleData(RoundEnvironment roundEnv) { + for (Element e : roundEnv.getRootElements()) { + if (e instanceof TypeElement) { + TypeElement currentClass = (TypeElement) e; + data.removeProvider(createProperQualifiedName(currentClass)); + } + } + } + + private void handleAnnotations(RoundEnvironment roundEnv) { + Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Provides.class); + for (Element e : elements) handleProvidingElement(e); + } + + private void handleProvidingElement(Element element) { + if (element.getKind() != ElementKind.CLASS) { + report(element, "is not a class definition"); + return; + } + + TypeElement elem = (TypeElement) element; + + Element enclosing = elem.getEnclosingElement(); + if (enclosing != null && enclosing.getKind() == ElementKind.CLASS && !elem.getModifiers().contains(Modifier.STATIC)) { + report(elem, "is a non-static inner class"); + return; + } + + boolean hasConstructors = false; + boolean hasNoArgsConstructor = false; + for (Element child : elem.getEnclosedElements()) { + if (child.getKind() != ElementKind.CONSTRUCTOR) continue; + ExecutableElement ee = (ExecutableElement) child; + hasConstructors = true; + if (ee.getParameters().isEmpty()) { + hasNoArgsConstructor = true; + break; + } + } + + if (hasConstructors && !hasNoArgsConstructor) { + report(elem, "has no no-args constructor"); + return; + } + + List<TypeMirror> spiTypes = new ArrayList<TypeMirror>(); + + for (AnnotationMirror annMirror : element.getAnnotationMirrors()) { + if (!getQualifiedTypeName(annMirror).contentEquals(Provides.class.getName())) continue; + for (Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annMirror.getElementValues().entrySet()) { + if (!entry.getKey().getSimpleName().contentEquals("value")) continue; + Object list = entry.getValue().getValue(); + if (list instanceof TypeMirror) spiTypes.add((TypeMirror) list); + else if (list instanceof List<?>) { + for (Object tm : (List<?>) list) { + if (tm instanceof AnnotationValue) tm = ((AnnotationValue) tm).getValue(); + if (tm instanceof TypeMirror) { + TypeMirror mirror = (TypeMirror) tm; + mirror = processingEnv.getTypeUtils().erasure(mirror); + spiTypes.add(mirror); + } + } + } + } + } + + TypeMirror superclass = elem.getSuperclass(); + if (spiTypes.isEmpty()) { + List<TypeMirror> qualifying = new ArrayList<TypeMirror>(); + qualifying.addAll(elem.getInterfaces()); + if (superclass != null && !toElement(superclass).getQualifiedName().contentEquals("java.lang.Object")) qualifying.add(superclass); + + if (qualifying.isEmpty()) { + report(elem, "is marked @Provides but implements/extends nothing"); + return; + } + if (qualifying.size() > 1) { + report(elem, "is marked @Provides but implements/extends multiple types; explicitly specify which interface(s) this provides for"); + return; + } + spiTypes.add(qualifying.get(0)); + } else { + Deque<TypeMirror> parentage = new ArrayDeque<TypeMirror>(); + parentage.addAll(elem.getInterfaces()); + parentage.add(superclass); + List<TypeMirror> needed = new ArrayList<TypeMirror>(); + needed.addAll(spiTypes); + while (!parentage.isEmpty() && !spiTypes.isEmpty()) { + TypeMirror parent = parentage.pollFirst(); + if (parent == null) continue; + needed.remove(parent); + parentage.addAll(processingEnv.getTypeUtils().directSupertypes(parent)); + } + if (!needed.isEmpty()) { + report(elem, "is marked as providing " + needed + " but does not implement it"); + return; + } + } + + for (TypeMirror spiType : spiTypes) { + String spiTypeName = createProperQualifiedName(toElement(spiType)); + String createProperQualifiedName = createProperQualifiedName(elem); + data.getService(spiTypeName).addProvider(createProperQualifiedName); + } + } + + private Name getQualifiedTypeName(AnnotationMirror mirror) { + Element elem = mirror.getAnnotationType().asElement(); + if (!(elem instanceof TypeElement)) return null; + return ((TypeElement) elem).getQualifiedName(); + } + + private TypeElement toElement(TypeMirror typeMirror) { + if (typeMirror instanceof DeclaredType) { + Element asElement = ((DeclaredType) typeMirror).asElement(); + if (asElement instanceof TypeElement) return (TypeElement) asElement; + } + return null; + } + + private void report(Element elem, String message) { + /* In eclipse, messages just seem to get ignored, so we throw instead. */ + if (Boolean.TRUE) throw new RuntimeException(elem.getSimpleName() + " " + message); + processingEnv.getMessager().printMessage(Kind.ERROR, elem.getSimpleName() + " " + message, elem); + } + + private String createProperQualifiedName(TypeElement provider) { + return processingEnv.getElementUtils().getBinaryName(provider).toString(); + } +} diff --git a/src/spiProcessor/lombok/spi/SpiProcessorCollector.java b/src/spiProcessor/lombok/spi/SpiProcessorCollector.java new file mode 100644 index 00000000..52e557f6 --- /dev/null +++ b/src/spiProcessor/lombok/spi/SpiProcessorCollector.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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.spi; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.util.Elements; +import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +class SpiProcessorCollector { + private final Map<String, SpiProcessorService> services = new HashMap<String, SpiProcessorService>(); + private final List<String> removed = new ArrayList<String>(); + + private final Elements elements; + private final Messager logger; + private final Filer filer; + + SpiProcessorCollector(ProcessingEnvironment processingEnv) { + this.elements = processingEnv.getElementUtils(); + this.logger = processingEnv.getMessager(); + this.filer = processingEnv.getFiler(); + } + + SpiProcessorCollector(Elements elements, Messager logger, Filer filer) { + if (elements == null) throw new NullPointerException("elements"); + if (logger == null) throw new NullPointerException("logger"); + if (filer == null) throw new NullPointerException("filer"); + this.elements = elements; + this.logger = logger; + this.filer = filer; + } + + SpiProcessorService getService(String serviceName) { + if (serviceName == null) throw new NullPointerException("serviceName"); + if (!services.containsKey(serviceName)) { + SpiProcessorService newService = new SpiProcessorService(serviceName); + CharSequence initialData = readInitialData(serviceName); + if (initialData != null) { + newService.addAllFromProvidersNameList(initialData.toString()); + for (String provider : removed) newService.removeProvider(provider); + } + services.put(serviceName, newService); + } + return services.get(serviceName); + } + + Collection<SpiProcessorService> services() { + return Collections.unmodifiableMap(services).values(); + } + + void removeProvider(String provider) { + if (provider == null) throw new NullPointerException("provider"); + removed.add(provider); + for (SpiProcessorService service : services.values()) service.removeProvider(provider); + } + + @Override public String toString() { + return services.values().toString(); + } + + private CharSequence readInitialData(String serviceName) { + String pathName = SpiProcessor.getRootPathOfServiceFiles() + serviceName; + FileObject resource; + + try { + resource = filer.getResource(StandardLocation.CLASS_OUTPUT, "", pathName); + } catch (Exception e) { + logger.printMessage(Kind.ERROR, SpiProcessor.toErrorMsg(e, pathName)); + return null; + } + + return SpiProcessorPersistence.readFilerResource(resource, logger, pathName); + } + + void stripProvidersWithoutSourceFile() { + for (SpiProcessorService s : services.values()) s.stripProvidersWithoutSourceFile(elements); + } +} diff --git a/src/spiProcessor/lombok/spi/SpiProcessorPersistence.java b/src/spiProcessor/lombok/spi/SpiProcessorPersistence.java new file mode 100644 index 00000000..c9bd745f --- /dev/null +++ b/src/spiProcessor/lombok/spi/SpiProcessorPersistence.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2021 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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.spi; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +class SpiProcessorPersistence { + private final String name; + private final String path; + final Filer filer; + private final Messager logger; + + SpiProcessorPersistence(String name, Filer filer, Messager logger) { + this.name = name; + this.logger = logger; + this.path = SpiProcessor.getRootPathOfServiceFiles(); + this.filer = filer; + } + + static CharSequence readFilerResource(FileObject resource, Messager logger, String pathName) { + try { + // Eclipse can't handle getCharContent, so we must use a reader... + return tryWithReader(resource); + } catch (FileNotFoundException e) { + return null; + } catch (IOException e) { + if ( + e.getClass().getName().equals("org.eclipse.core.internal.resources.ResourceException") && + e.getMessage() != null && + e.getMessage().endsWith("does not exist.")) { + + return null; + } + + logger.printMessage(Kind.ERROR, SpiProcessor.toErrorMsg(e, pathName)); + return null; + } catch (Exception other) { + // otherwise, probably javac: Some versions don't support the `openReader` method. + try { + return resource.getCharContent(true); + } catch (FileNotFoundException e) { + return null; + } catch (IOException e) { + logger.printMessage(Kind.ERROR, SpiProcessor.toErrorMsg(e, pathName)); + return null; + } + } + } + + private static CharSequence tryWithReader(FileObject resource) throws IOException { + StringBuilder sb = new StringBuilder(); + Reader raw = resource.openReader(true); + try { + BufferedReader in = new BufferedReader(raw); + for (String line = in.readLine(); line != null; line = in.readLine()) sb.append(line).append('\n'); + return sb; + } finally { + if (raw != null) raw.close(); + } + } + + Collection<String> tryFind() { + File dir = determineOutputLocation(); + if (dir == null || !dir.isDirectory()) return Collections.emptyList(); + List<String> out = new ArrayList<String>(); + for (File p : dir.listFiles()) { + if (!p.isFile()) continue; + out.add(p.getName()); + } + return out; + } + + private File determineOutputLocation() { + FileObject resource; + try { + resource = filer.createResource(StandardLocation.CLASS_OUTPUT, "META-INF", "locator.tmp"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + // Could happen + return null; + } catch (IOException e) { + e.printStackTrace(); + logger.printMessage(Kind.NOTE, "IOException while determining output location: " + e.getMessage()); + return null; + } catch (IllegalArgumentException e) { + e.printStackTrace(); + // Happens when the path is invalid. For instance absolute or relative to a path + // not part of the class output folder. + // + // Due to a bug in javac for Linux, this also occurs when no output path is specified + // for javac using the -d parameter. + // See http://forums.sun.com/thread.jspa?threadID=5240999&tstart=45 + // and http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6647996 + + return null; + } + + URI uri = resource.toUri(); + return new File(new File(uri).getParentFile(), "services"); + } + + void write(String serviceName, String value) throws IOException { + FileObject output = filer.createResource(StandardLocation.CLASS_OUTPUT, "", path + serviceName); + Writer writer = output.openWriter(); + writer.write("# Generated by " + name + "\n"); + writer.write("# " + new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.US).format(new Date()) + "\n"); + writer.write(value); + writer.close(); + } +} diff --git a/src/spiProcessor/lombok/spi/SpiProcessorService.java b/src/spiProcessor/lombok/spi/SpiProcessorService.java new file mode 100644 index 00000000..be8d5a21 --- /dev/null +++ b/src/spiProcessor/lombok/spi/SpiProcessorService.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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.spi; + +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import javax.lang.model.util.Elements; + +final class SpiProcessorService { + private final String name; + private final Set<String> providers = new TreeSet<String>(); + + SpiProcessorService(String name) { + if (name == null) throw new NullPointerException("name"); + this.name = name; + } + + public String getName() { + return name; + } + + void addProvider(String className) { + if (className == null) throw new NullPointerException("className"); + providers.add(className); + } + + boolean removeProvider(String provider) { + return providers.remove(provider); + } + + String toProvidersListFormat() { + return providers.stream().collect(Collectors.joining("\n")); + } + + void addAllFromProvidersNameList(String in) { + for (String line : in.split("\\n")) { + String[] content = line.split("#", 2); + if (content.length == 0) continue; + String trimmed = content[0].trim(); + String[] elems = trimmed.split("\\s+", 2); + if (elems.length == 0 || elems[0].isEmpty()) continue; + String cn = elems[0]; + addProvider(cn); + } + } + + void stripProvidersWithoutSourceFile(Elements elements) { + Iterator<String> it = providers.iterator(); + while (it.hasNext()) { + if (!sourceExists(elements, it.next())) it.remove(); + } + } + + private boolean sourceExists(Elements elements, String typeName) { + return elements.getTypeElement(typeName) != null; + } + + @Override public String toString() { + return name + " = " + providers; + } +} diff --git a/src/stubs/com/sun/tools/javac/code/Symbol.java b/src/stubs/com/sun/tools/javac/code/Symbol.java index 15b04148..f389f1a8 100644 --- a/src/stubs/com/sun/tools/javac/code/Symbol.java +++ b/src/stubs/com/sun/tools/javac/code/Symbol.java @@ -24,6 +24,7 @@ import com.sun.tools.javac.util.Name; public abstract class Symbol implements Element { public Type type; public Name name; + public Symbol owner; public long flags() { return 0; } public boolean isStatic() { return false; } @@ -62,6 +63,7 @@ public abstract class Symbol implements Element { public static class VarSymbol extends Symbol implements VariableElement { public Type type; + public int adr; public VarSymbol(long flags, Name name, Type type, Symbol owner) { } @Override public ElementKind getKind() { return null; } diff --git a/src/stubs/com/sun/tools/javac/main/Arguments.java b/src/stubs/com/sun/tools/javac/main/Arguments.java index ea866b6e..3d91734c 100644 --- a/src/stubs/com/sun/tools/javac/main/Arguments.java +++ b/src/stubs/com/sun/tools/javac/main/Arguments.java @@ -10,4 +10,7 @@ public class Arguments { public void init(String ownName, String... argv) {} public Map<Option, String> getDeferredFileManagerOptions() { return null; } public boolean validate() { return false; } + + // JDK15 + public void init(String ownName, Iterable<String> args) {} } diff --git a/src/stubs/com/sun/tools/javac/parser/JavaTokenizer.java b/src/stubs/com/sun/tools/javac/parser/JavaTokenizer.java index 7d5bbcb1..cde6b325 100644 --- a/src/stubs/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/stubs/com/sun/tools/javac/parser/JavaTokenizer.java @@ -1,11 +1,15 @@ package com.sun.tools.javac.parser; +import java.nio.CharBuffer; + import com.sun.tools.javac.parser.Tokens.Comment; import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle; public class JavaTokenizer { + // used before java 16 protected UnicodeReader reader; + // used before java 16 protected JavaTokenizer(ScannerFactory fac, UnicodeReader reader) { } @@ -16,4 +20,17 @@ public class JavaTokenizer { protected Comment processComment(int pos, int endPos, CommentStyle style) { return null; } + + // used in java 16 + protected JavaTokenizer(ScannerFactory fac, char[] buf, int inputLength) { + } + + // used in java 16 + protected JavaTokenizer(ScannerFactory fac, CharBuffer buf) { + } + + // introduced in java 16 + protected int position() { + return -1; + } } 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/utils/lombok/core/FieldAugment.java b/src/utils/lombok/core/FieldAugment.java index 4a32ad04..0982bcb5 100644 --- a/src/utils/lombok/core/FieldAugment.java +++ b/src/utils/lombok/core/FieldAugment.java @@ -24,6 +24,7 @@ package lombok.core; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; @@ -69,6 +70,10 @@ public abstract class FieldAugment<T, F> { checkNotNull(fieldType, "fieldType"); checkNotNull(name, "name"); + if (type.isInterface()) { + return new InterfaceFieldAugment<T, F>(name, fieldType); + } + @SuppressWarnings("unchecked") F defaultValue = (F) getDefaultValue(fieldType); FieldAugment<T, F> ret = tryCreateReflectionAugment(type, fieldType, name, defaultValue); @@ -175,6 +180,54 @@ public abstract class FieldAugment<T, F> { */ public abstract F compareAndSet(T object, F expected, F value); + private static final class InterfaceFieldAugment<T, F> extends FieldAugment<T, F> { + private final String name; + private final Class<? super F> fieldType; + + private Map<Class<T>, FieldAugment<T, F>> map = new HashMap<Class<T>, FieldAugment<T,F>>(); + + private InterfaceFieldAugment(String name, Class<? super F> fieldType) { + this.name = name; + this.fieldType = fieldType; + } + + private synchronized FieldAugment<T, F> getDelegate(T object) { + @SuppressWarnings("unchecked") + Class<T> c = (Class<T>) object.getClass(); + + FieldAugment<T,F> fieldAugment = map.get(c); + if (fieldAugment == null) { + fieldAugment = augment(c, fieldType, name); + map.put(c, fieldAugment); + } + return fieldAugment; + } + + @Override public F get(T object) { + return getDelegate(object).get(object); + } + + @Override public F getAndSet(T object, F value) { + return getDelegate(object).getAndSet(object, value); + } + + @Override public F clear(T object) { + return getDelegate(object).clear(object); + } + + @Override public F compareAndClear(T object, F expected) { + return getDelegate(object).compareAndClear(object, expected); + } + + @Override public F setIfAbsent(T object, F value) { + return getDelegate(object).setIfAbsent(object, value); + } + + @Override public F compareAndSet(T object, F expected, F value) { + return getDelegate(object).compareAndSet(object, expected, value); + } + } + private static class ReflectionFieldAugment<T, F> extends FieldAugment<T, F> { private final Object lock = new Object(); private final Field field; diff --git a/src/utils/lombok/eclipse/Eclipse.java b/src/utils/lombok/eclipse/Eclipse.java index cda75da5..ac15f90b 100644 --- a/src/utils/lombok/eclipse/Eclipse.java +++ b/src/utils/lombok/eclipse/Eclipse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2019 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -60,6 +60,11 @@ public class Eclipse { */ public static final int ECLIPSE_DO_NOT_TOUCH_FLAG = ASTNode.Bit24; + /* This section includes flags that would ordinarily be in ClassFileConstants, but which are 'too new' (we don't compile against older versions of ecj/eclipse for compatibility). */ + public static final int AccRecord = ASTNode.Bit25; + public static final int IsCanonicalConstructor = ASTNode.Bit10; // For record declarations, and presumably later on any constructor matching the destructor. + public static final int IsImplicit = ASTNode.Bit11; // the generated statements in the compact constructor of a record. + private static final Pattern SPLIT_AT_DOT = Pattern.compile("\\."); private Eclipse() { @@ -239,18 +244,14 @@ public class Eclipse { for (Field f : CompilerOptions.class.getDeclaredFields()) { try { - if (f.getName().startsWith("VERSION_")) { - String version = f.getName().substring("VERSION_".length()); - Integer versionNumber = null; - if (version.startsWith("1_")) { - versionNumber = Integer.parseInt(version.substring("1_".length())); - } else if (version.length() <= 2) { - versionNumber = Integer.parseInt(version); - } - if (versionNumber != null) { - ecjCompilerVersionCached = Math.max(ecjCompilerVersionCached, versionNumber); - } - } + String fName = f.getName(); + String versionNumber = null; + if (fName.startsWith("VERSION_1_")) { + versionNumber = fName.substring("VERSION_1_".length()); + } else if (fName.startsWith("VERSION_")) { + versionNumber = fName.substring("VERSION_".length()); + } else continue; + ecjCompilerVersionCached = Math.max(ecjCompilerVersionCached, Integer.parseInt(versionNumber)); } catch (Exception ignore) {} } diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java index cec43705..c0bda93c 100644 --- a/src/utils/lombok/javac/Javac.java +++ b/src/utils/lombok/javac/Javac.java @@ -75,6 +75,12 @@ public class Javac { private static final AtomicInteger compilerVersion = new AtomicInteger(-1); + /* This section includes flags that would ordinarily be in Flags, but which are 'too new' (we don't compile against older versions of javac for compatibility). */ + public static final long RECORD = 1L << 61; // ClassSymbols, MethodSymbols, VarSymbols (Marks types as being records, as well as the 'fields' in the compact declaration, and the canonical constructor) + public static final long COMPACT_RECORD_CONSTRUCTOR = 1L << 51; // MethodSymbols (the 'implicit' many-args constructor that records have) + public static final long UNINITIALIZED_FIELD = 1L << 51; // VarSymbols (To identify fields that the compact record constructor won't initialize) + public static final long GENERATED_MEMBER = 1L << 24; // MethodSymbols, VarSymbols (marks methods and the constructor generated in records) + /** * Returns the version of this java compiler, i.e. the JDK that it shipped in. For example, for javac v1.7, this returns {@code 7}. */ @@ -279,8 +285,20 @@ public class Javac { return null; } + /** + * Checks if the javadoc comment associated with {@code tree} has a position set. + * + * Returns true if there is no javadoc comment on the node, or it has position (position isn't -1). + */ + public static boolean validateDocComment(JCCompilationUnit cu, JCTree tree) { + Object dc = getDocComments(cu); + if (!instanceOfDocCommentTable(dc)) return true; + return JavadocOps_8.validateJavadoc(dc, tree); + } + @SuppressWarnings("unchecked") public static void setDocComment(JCCompilationUnit cu, JCTree node, String javadoc) { + if (javadoc == null) return; Object dc = getDocComments(cu); if (dc instanceof Map) { ((Map<JCTree, String>) dc).put(node, javadoc); @@ -301,6 +319,12 @@ public class Javac { return javadoc.getText(); } + public static boolean validateJavadoc(Object dc, JCTree node) { + DocCommentTable dct = (DocCommentTable) dc; + Comment javadoc = dct.getComment(node); + return javadoc == null || javadoc.getText() == null || javadoc.getSourcePos(0) >= 0; + } + static void setJavadoc(Object dc, JCTree node, String javadoc) { DocCommentTable dct = (DocCommentTable) dc; Comment newCmt = createJavadocComment(javadoc, node); @@ -314,7 +338,7 @@ public class Javac { } @Override public int getSourcePos(int index) { - return -1; + return field == null ? -1 : field.getStartPosition(); } @Override public CommentStyle getStyle() { diff --git a/src/utils/lombok/javac/JavacTreeMaker.java b/src/utils/lombok/javac/JavacTreeMaker.java index 15a11151..30d71606 100644 --- a/src/utils/lombok/javac/JavacTreeMaker.java +++ b/src/utils/lombok/javac/JavacTreeMaker.java @@ -24,6 +24,7 @@ package lombok.javac; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -106,7 +107,19 @@ public class JavacTreeMaker { return this; } - private static class MethodId<J> { + private static final class FieldId<J> { + private final Class<?> owner; + private final String name; + private final Class<J> fieldType; + + FieldId(Class<?> owner, String name, Class<J> fieldType) { + this.owner = owner; + this.name = name; + this.fieldType = fieldType; + } + } + + private static final class MethodId<J> { private final Class<?> owner; private final String name; private final Class<J> returnType; @@ -332,9 +345,79 @@ public class JavacTreeMaker { throw new InternalError("Not found: " + name); } - private static final Object METHOD_NOT_FOUND = new Object[0]; - private static final Object METHOD_MULTIPLE_FOUND = new Object[0]; + static <J> FieldId<J> FieldId(Class<?> owner, String name, Class<J> fieldType) { + return new FieldId<J>(owner, name, fieldType); + } + + private static final ConcurrentHashMap<FieldId<?>, Object> FIELD_CACHE = new ConcurrentHashMap<FieldId<?>, Object>(); + + private static boolean has(FieldId<?> f) { + Object field = FIELD_CACHE.get(f); + if (field == REFLECTIVE_ITEM_NOT_FOUND) return false; + if (field instanceof Field) return true; + + try { + return getFromCache(f) != REFLECTIVE_ITEM_NOT_FOUND; + } catch (IllegalStateException e) { + return false; + } + } + + private static <J> J get(Object owner, FieldId<J> f) { + Field field = getFromCache(f); + try { + return f.fieldType.cast(field.get(owner)); + } catch (IllegalAccessException e) { + throw Javac.sneakyThrow(e); + } + } + + private static <J> void set(Object owner, FieldId<J> f, J val) { + Field field = getFromCache(f); + try { + field.set(owner, val); + } catch (IllegalAccessException e) { + throw Javac.sneakyThrow(e); + } catch (IllegalArgumentException e) { + System.err.println("Type mismatch for: " + field); + throw e; + } + } + + private static Field getFromCache(FieldId<?> f) { + Object s = FIELD_CACHE.get(f); + if (s == null) s = addToCache(f); + if (s == REFLECTIVE_ITEM_NOT_FOUND) throw new IllegalStateException("Lombok TreeMaker frontend issue: no match when looking for field: " + f); + return (Field) s; + } + + private static Object addToCache(FieldId<?> f) { + for (Field field : f.owner.getDeclaredFields()) { + if (f.name.equals(field.getName())) { + if (!Modifier.isPublic(field.getModifiers())) field.setAccessible(true); + return FIELD_CACHE.putIfAbsent(f, field); + } + } + + return FIELD_CACHE.putIfAbsent(f, REFLECTIVE_ITEM_NOT_FOUND); + } + + private static final Object REFLECTIVE_ITEM_NOT_FOUND = new Object[0]; + private static final Object REFLECTIVE_ITEM_MULTIPLE_FOUND = new Object[0]; private static final ConcurrentHashMap<MethodId<?>, Object> METHOD_CACHE = new ConcurrentHashMap<MethodId<?>, Object>(); + + private boolean has(MethodId<?> m) { + Object method = METHOD_CACHE.get(m); + if (method == REFLECTIVE_ITEM_NOT_FOUND) return false; + if (method instanceof Method) return true; + + try { + return getFromCache(m) != REFLECTIVE_ITEM_NOT_FOUND; + } catch (IllegalStateException e) { + return false; + } + } + private <J> J invoke(MethodId<J> m, Object... args) { return invokeAny(tm, m, args); } @@ -351,8 +434,8 @@ public class JavacTreeMaker { } catch (IllegalAccessException e) { throw Javac.sneakyThrow(e); } catch (IllegalArgumentException e) { - System.err.println(method); - throw Javac.sneakyThrow(e); + System.err.println("Type mismatch for: " + method); + throw e; } } @@ -366,8 +449,8 @@ public class JavacTreeMaker { private static Method getFromCache(MethodId<?> m) { Object s = METHOD_CACHE.get(m); if (s == null) s = addToCache(m); - if (s == METHOD_MULTIPLE_FOUND) throw new IllegalStateException("Lombok TreeMaker frontend issue: multiple matches when looking for method: " + m); - if (s == METHOD_NOT_FOUND) throw new IllegalStateException("Lombok TreeMaker frontend issue: no match when looking for method: " + m); + if (s == REFLECTIVE_ITEM_MULTIPLE_FOUND) throw new IllegalStateException("Lombok TreeMaker frontend issue: multiple matches when looking for method: " + m); + if (s == REFLECTIVE_ITEM_NOT_FOUND) throw new IllegalStateException("Lombok TreeMaker frontend issue: no match when looking for method: " + m); return (Method) s; } @@ -391,13 +474,13 @@ public class JavacTreeMaker { } if (found == null) found = method; else { - METHOD_CACHE.putIfAbsent(m, METHOD_MULTIPLE_FOUND); - return METHOD_MULTIPLE_FOUND; + METHOD_CACHE.putIfAbsent(m, REFLECTIVE_ITEM_MULTIPLE_FOUND); + return REFLECTIVE_ITEM_MULTIPLE_FOUND; } } if (found == null) { - METHOD_CACHE.putIfAbsent(m, METHOD_NOT_FOUND); - return METHOD_NOT_FOUND; + METHOD_CACHE.putIfAbsent(m, REFLECTIVE_ITEM_NOT_FOUND); + return REFLECTIVE_ITEM_NOT_FOUND; } Permit.setAccessible(found); Object marker = METHOD_CACHE.putIfAbsent(m, found); @@ -431,8 +514,12 @@ public class JavacTreeMaker { //javac versions: 8 private static final MethodId<JCMethodDecl> MethodDefWithRecvParam = MethodId("MethodDef", JCMethodDecl.class, JCModifiers.class, Name.class, JCExpression.class, List.class, JCVariableDecl.class, List.class, List.class, JCBlock.class, JCExpression.class); - public JCMethodDecl MethodDef(JCModifiers mods, Name name, JCExpression resType, List<JCTypeParameter> typarams, JCVariableDecl recvparam, List<JCVariableDecl> params, List<JCExpression> thrown, JCBlock body, JCExpression defaultValue) { - return invoke(MethodDefWithRecvParam, mods, name, resType, recvparam, typarams, params, thrown, body, defaultValue); + public boolean hasMethodDefWithRecvParam() { + return has(MethodDefWithRecvParam); + } + + public JCMethodDecl MethodDefWithRecvParam(JCModifiers mods, Name name, JCExpression resType, List<JCTypeParameter> typarams, JCVariableDecl recvparam, List<JCVariableDecl> params, List<JCExpression> thrown, JCBlock body, JCExpression defaultValue) { + return invoke(MethodDefWithRecvParam, mods, name, resType, typarams, recvparam, params, thrown, body, defaultValue); } //javac versions: 6-8 @@ -878,4 +965,18 @@ public class JavacTreeMaker { public JCExpression Type(Type type) { return invoke(Type, type); } + + private static final FieldId<JCVariableDecl> MethodDecl_recvParam = FieldId(JCMethodDecl.class, "recvparam", JCVariableDecl.class); + //javac versions: 8+ + public boolean hasReceiverParameter() { + return has(MethodDecl_recvParam); + } + + public JCVariableDecl getReceiverParameter(JCMethodDecl method) { + return get(method, MethodDecl_recvParam); + } + + public void setReceiverParameter(JCMethodDecl method, JCVariableDecl param) { + set(method, MethodDecl_recvParam, param); + } }
\ No newline at end of file diff --git a/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java b/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java index cb0d2e12..f29f501b 100644 --- a/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java +++ b/src/utils/lombok/javac/java8/CommentCollectingScannerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2019 The Project Lombok Authors. + * Copyright (C) 2011-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -72,18 +72,31 @@ public class CommentCollectingScannerFactory extends ScannerFactory { super(context); } + @SuppressWarnings("all") @Override public Scanner newScanner(CharSequence input, boolean keepDocComments) { - if (input instanceof CharBuffer) { - CharBuffer buf = (CharBuffer) input; - return new CommentCollectingScanner(this, new CommentCollectingTokenizer(this, buf, findTextBlocks)); + char[] array; + int limit; + if (input instanceof CharBuffer && ((CharBuffer) input).hasArray()) { + CharBuffer cb = (CharBuffer) input; + cb.compact().flip(); + array = cb.array(); + limit = cb.limit(); + } else { + array = input.toString().toCharArray(); + limit = array.length; + } + if (array.length == limit) { + // work around a bug where the last comment in a file falls away in this case. + char[] d = new char[limit + 1]; + System.arraycopy(array, 0, d, 0, limit); + array = d; } - char[] array = input.toString().toCharArray(); - return newScanner(array, array.length, keepDocComments); + return newScanner(array, limit, keepDocComments); } @Override public Scanner newScanner(char[] input, int inputLength, boolean keepDocComments) { - return new CommentCollectingScanner(this, new CommentCollectingTokenizer(this, input, inputLength, findTextBlocks)); + return new CommentCollectingScanner(this, CommentCollectingTokenizer.create(this, input, inputLength, findTextBlocks)); } } diff --git a/src/utils/lombok/javac/java8/CommentCollectingTokenizer.java b/src/utils/lombok/javac/java8/CommentCollectingTokenizer.java index d7b1d569..4a31fc81 100644 --- a/src/utils/lombok/javac/java8/CommentCollectingTokenizer.java +++ b/src/utils/lombok/javac/java8/CommentCollectingTokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2020 The Project Lombok Authors. + * Copyright (C) 2013-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,36 +23,51 @@ package lombok.javac.java8; import java.nio.CharBuffer; -import lombok.javac.CommentInfo; -import lombok.javac.CommentInfo.EndConnection; -import lombok.javac.CommentInfo.StartConnection; - import com.sun.tools.javac.parser.JavaTokenizer; import com.sun.tools.javac.parser.ScannerFactory; import com.sun.tools.javac.parser.Tokens.Comment; -import com.sun.tools.javac.parser.Tokens.Token; import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle; +import com.sun.tools.javac.parser.Tokens.Token; import com.sun.tools.javac.parser.UnicodeReader; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; +import lombok.javac.CommentInfo; +import lombok.javac.CommentInfo.EndConnection; +import lombok.javac.CommentInfo.StartConnection; + class CommentCollectingTokenizer extends JavaTokenizer { + + private static final boolean tokenizerIsUnicodeReader = JavaTokenizer.class.getSuperclass().getSimpleName().equals("UnicodeReader"); + private int prevEndPosition = 0; private final ListBuffer<CommentInfo> comments = new ListBuffer<CommentInfo>(); private final ListBuffer<Integer> textBlockStarts; private int endComment = 0; - CommentCollectingTokenizer(ScannerFactory fac, char[] buf, int inputLength, boolean findTextBlocks) { + static CommentCollectingTokenizer create(ScannerFactory fac, char[] buf, int inputLength, boolean findTextBlocks) { + if (tokenizerIsUnicodeReader) { + return new CommentCollectingTokenizer(fac, buf, inputLength, findTextBlocks, true); + } + return new CommentCollectingTokenizer(fac, buf, inputLength, findTextBlocks); + } + + // pre java 16 + private CommentCollectingTokenizer(ScannerFactory fac, char[] buf, int inputLength, boolean findTextBlocks) { super(fac, new PositionUnicodeReader(fac, buf, inputLength)); textBlockStarts = findTextBlocks ? new ListBuffer<Integer>() : null; } - CommentCollectingTokenizer(ScannerFactory fac, CharBuffer buf, boolean findTextBlocks) { - super(fac, new PositionUnicodeReader(fac, buf)); + // from java 16 + private CommentCollectingTokenizer(ScannerFactory fac, char[] buf, int inputLength, boolean findTextBlocks, boolean java16Signature) { + super(fac, buf, inputLength); textBlockStarts = findTextBlocks ? new ListBuffer<Integer>() : null; } int pos() { + if (tokenizerIsUnicodeReader) { + return position(); + } return ((PositionUnicodeReader) reader).pos(); } @@ -60,8 +75,8 @@ class CommentCollectingTokenizer extends JavaTokenizer { Token token = super.readToken(); prevEndPosition = pos(); if (textBlockStarts != null && (prevEndPosition - token.pos > 5) && token.getClass().getName().endsWith("$StringToken")) { - char[] start = reader.getRawCharacters(token.pos, token.pos + 3); - if (start[0] == '"' && start[1] == '"' && start[2] == '"') textBlockStarts.add(token.pos); + char[] start = reader().getRawCharacters(token.pos, token.pos + 3); + if (start[0] == '"' && start[1] == '"' && start[2] == '"') textBlockStarts.append(token.pos); } return token; } @@ -70,7 +85,7 @@ class CommentCollectingTokenizer extends JavaTokenizer { protected Comment processComment(int pos, int endPos, CommentStyle style) { int prevEndPos = Math.max(prevEndPosition, endComment); endComment = endPos; - String content = new String(reader.getRawCharacters(pos, endPos)); + String content = new String(reader().getRawCharacters(pos, endPos)); StartConnection start = determineStartConnection(prevEndPos, pos); EndConnection end = determineEndConnection(endPos); @@ -85,7 +100,7 @@ class CommentCollectingTokenizer extends JavaTokenizer { for (int i = pos;; i++) { char c; try { - c = reader.getRawCharacters(i, i + 1)[0]; + c = reader().getRawCharacters(i, i + 1)[0]; } catch (IndexOutOfBoundsException e) { c = '\n'; } @@ -104,7 +119,7 @@ class CommentCollectingTokenizer extends JavaTokenizer { if (from == to) { return StartConnection.DIRECT_AFTER_PREVIOUS; } - char[] between = reader.getRawCharacters(from, to); + char[] between = reader().getRawCharacters(from, to); if (isNewLine(between[between.length - 1])) { return StartConnection.START_OF_LINE; } @@ -128,6 +143,13 @@ class CommentCollectingTokenizer extends JavaTokenizer { return textBlockStarts == null ? List.<Integer>nil() : textBlockStarts.toList(); } + private UnicodeReader reader() { + if (tokenizerIsUnicodeReader) { + return (UnicodeReader) (Object) this; + } + return reader; + } + static class PositionUnicodeReader extends UnicodeReader { protected PositionUnicodeReader(ScannerFactory sf, char[] input, int inputLength) { super(sf, input, inputLength); diff --git a/src/utils/lombok/permit/Permit.java b/src/utils/lombok/permit/Permit.java index 407c3922..2854706e 100644 --- a/src/utils/lombok/permit/Permit.java +++ b/src/utils/lombok/permit/Permit.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-20199 The Project Lombok Authors. + * Copyright (C) 2018-2019 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,8 +24,17 @@ package lombok.permit; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.URL; +import javax.tools.JavaFileManager; + +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; + +import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.util.List; @@ -113,6 +122,14 @@ public class Permit { return setAccessible(m); } + public static Method permissiveGetMethod(Class<?> c, String mName, Class<?>... parameterTypes) { + try { + return getMethod(c, mName, parameterTypes); + } catch (Exception ignore) { + return null; + } + } + public static Field getField(Class<?> c, String fName) throws NoSuchFieldException { Field f = null; Class<?> oc = c; @@ -158,4 +175,175 @@ public class Permit { return null; } } + + public static boolean isDebugReflection() { + return !"false".equals(System.getProperty("lombok.debug.reflection", "false")); + } + + public static void handleReflectionDebug(Throwable t, Throwable initError) { + if (!isDebugReflection()) return; + + System.err.println("** LOMBOK REFLECTION exception: " + t.getClass() + ": " + (t.getMessage() == null ? "(no message)" : t.getMessage())); + t.printStackTrace(System.err); + if (initError != null) { + System.err.println("*** ADDITIONALLY, exception occurred setting up reflection: "); + initError.printStackTrace(System.err); + } + } + + public static Object invoke(Method m, Object receiver, Object... args) throws IllegalAccessException, InvocationTargetException { + return invoke(null, m, receiver, args); + } + + public static Object invoke(Throwable initError, Method m, Object receiver, Object... args) throws IllegalAccessException, InvocationTargetException { + try { + return m.invoke(receiver, args); + } catch (IllegalAccessException e) { + handleReflectionDebug(e, initError); + throw e; + } catch (RuntimeException e) { + handleReflectionDebug(e, initError); + throw e; + } catch (Error e) { + handleReflectionDebug(e, initError); + throw e; + } + } + + public static Object invokeSneaky(Method m, Object receiver, Object... args) { + return invokeSneaky(null, m, receiver, args); + } + + public static Object invokeSneaky(Throwable initError, Method m, Object receiver, Object... args) { + try { + return m.invoke(receiver, args); + } catch (NoClassDefFoundError e) { + handleReflectionDebug(e, initError); + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + return null; + } catch (NullPointerException e) { + handleReflectionDebug(e, initError); + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + return null; + } catch (IllegalAccessException e) { + handleReflectionDebug(e, initError); + throw sneakyThrow(e); + } catch (InvocationTargetException e) { + throw sneakyThrow(e.getCause()); + } catch (RuntimeException e) { + handleReflectionDebug(e, initError); + throw e; + } catch (Error e) { + handleReflectionDebug(e, initError); + throw e; + } + } + + public static <T> T newInstance(Constructor<T> c, Object... args) throws IllegalAccessException, InvocationTargetException, InstantiationException { + return newInstance(null, c, args); + } + + public static <T> T newInstance(Throwable initError, Constructor<T> c, Object... args) throws IllegalAccessException, InvocationTargetException, InstantiationException { + try { + return c.newInstance(args); + } catch (IllegalAccessException e) { + handleReflectionDebug(e, initError); + throw e; + } catch (InstantiationException e) { + handleReflectionDebug(e, initError); + throw e; + } catch (RuntimeException e) { + handleReflectionDebug(e, initError); + throw e; + } catch (Error e) { + handleReflectionDebug(e, initError); + throw e; + } + } + + public static <T> T newInstanceSneaky(Constructor<T> c, Object... args) { + return newInstanceSneaky(null, c, args); + } + + public static <T> T newInstanceSneaky(Throwable initError, Constructor<T> c, Object... args) { + try { + return c.newInstance(args); + } catch (NoClassDefFoundError e) { + handleReflectionDebug(e, initError); + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + return null; + } catch (NullPointerException e) { + handleReflectionDebug(e, initError); + //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly + //do anything useful here. + return null; + } catch (IllegalAccessException e) { + handleReflectionDebug(e, initError); + throw sneakyThrow(e); + } catch (InstantiationException e) { + handleReflectionDebug(e, initError); + throw sneakyThrow(e); + } catch (InvocationTargetException e) { + throw sneakyThrow(e.getCause()); + } catch (RuntimeException e) { + handleReflectionDebug(e, initError); + throw e; + } catch (Error e) { + handleReflectionDebug(e, initError); + throw e; + } + } + + public static Object get(Field f, Object receiver) throws IllegalAccessException { + try { + return f.get(receiver); + } catch (IllegalAccessException e) { + handleReflectionDebug(e, null); + throw e; + } catch (RuntimeException e) { + handleReflectionDebug(e, null); + throw e; + } catch (Error e) { + handleReflectionDebug(e, null); + throw e; + } + } + + public static void set(Field f, Object receiver, Object newValue) throws IllegalAccessException { + try { + f.set(receiver, newValue); + } catch (IllegalAccessException e) { + handleReflectionDebug(e, null); + throw e; + } catch (RuntimeException e) { + handleReflectionDebug(e, null); + throw e; + } catch (Error e) { + handleReflectionDebug(e, null); + throw e; + } + } + + public static void reportReflectionProblem(Throwable initError, String msg) { + if (!isDebugReflection()) return; + System.err.println("** LOMBOK REFLECTION issue: " + msg); + if (initError != null) { + System.err.println("*** ADDITIONALLY, exception occurred setting up reflection: "); + initError.printStackTrace(System.err); + } + } + + public static RuntimeException sneakyThrow(Throwable t) { + if (t == null) throw new NullPointerException("t"); + return Permit.<RuntimeException>sneakyThrow0(t); + } + + @SuppressWarnings("unchecked") + private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T { + throw (T)t; + } + } diff --git a/src/utils/lombok/permit/dummy/Child.java b/src/utils/lombok/permit/dummy/Child.java new file mode 100644 index 00000000..c189ee37 --- /dev/null +++ b/src/utils/lombok/permit/dummy/Child.java @@ -0,0 +1,9 @@ +package lombok.permit.dummy; + +@SuppressWarnings("all") +public abstract class Child extends Parent { + private transient volatile boolean foo; + private transient volatile Object[] bar; + private transient volatile Object baz; + +} diff --git a/src/utils/lombok/permit/dummy/GrandChild.java b/src/utils/lombok/permit/dummy/GrandChild.java new file mode 100644 index 00000000..ef182aa7 --- /dev/null +++ b/src/utils/lombok/permit/dummy/GrandChild.java @@ -0,0 +1,19 @@ +package lombok.permit.dummy; + +@SuppressWarnings("all") +public final class GrandChild extends Child { + private Class<?> a; + private int b; + private String c; + private Class<?> d; + private Class<?>[] e; + private Class<?>[] f; + private int g; + private transient String h; + private transient Object i; + private byte[] j; + private byte[] k; + private byte[] l; + private volatile Object m; + private Object n; +} diff --git a/src/utils/lombok/permit/dummy/Parent.java b/src/utils/lombok/permit/dummy/Parent.java new file mode 100644 index 00000000..33928aeb --- /dev/null +++ b/src/utils/lombok/permit/dummy/Parent.java @@ -0,0 +1,12 @@ +package lombok.permit.dummy; + +import java.io.OutputStream; + +@SuppressWarnings("all") +public class Parent { + boolean first; + static final Object staticObj = OutputStream.class; + volatile Object second; + private static volatile boolean staticSecond; + private static volatile boolean staticThird; +} diff --git a/src/utils/lombok/permit/dummy/package-info.java b/src/utils/lombok/permit/dummy/package-info.java new file mode 100644 index 00000000..87ca839a --- /dev/null +++ b/src/utils/lombok/permit/dummy/package-info.java @@ -0,0 +1,8 @@ +/** + * This package recreates the type hierarchy of {@code java.lang.reflect.AccessibleObject} and friends (such as {@code java.lang.reflect.Method}); + * its purpose is to allow us to ask {@code sun.misc.internal.Unsafe} about the exact offset of the {@code override} field of {@code AccessibleObject}; + * asking about that field directly doesn't work after jdk14, presumably because the fields of AO are expressly hidden somehow. + * + * NB: It's usually 12, on the vast majority of OS, VM, and architecture combos. + */ +package lombok.permit.dummy; |