diff options
Diffstat (limited to 'src')
143 files changed, 3007 insertions, 1208 deletions
diff --git a/src/ant/lombok/ant/SimpleTestFormatter.java b/src/ant/lombok/ant/SimpleTestFormatter.java new file mode 100644 index 00000000..e137d822 --- /dev/null +++ b/src/ant/lombok/ant/SimpleTestFormatter.java @@ -0,0 +1,87 @@ +package lombok.ant; + +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.Arrays; + +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; + private Test lastMarked = null; + + @Override + public void addError(Test test, Throwable error) { + lastMarked = test; + logResult(test, "ERR"); + printThrowable(error, false, 2); + } + + private void printThrowable(Throwable throwable, boolean cause, int indent) { + String msg = throwable.getMessage(); + char[] prefixChars = new char[indent]; + Arrays.fill(prefixChars, ' '); + String prefix = new String(prefixChars); + + if (msg == null || msg.isEmpty()) { + out.println(prefix + (cause ? "Caused by " : "") + throwable.getClass()); + } else { + out.println(prefix + (cause ? "Caused by " : "") + throwable.getClass() + ": " + msg); + } + StackTraceElement[] elems = throwable.getStackTrace(); + if (elems != null) for (StackTraceElement elem : elems) { + out.println(prefix + " " + elem); + } + + Throwable c = throwable.getCause(); + if (c != null) printThrowable(c, true, indent + 2); + } + + @Override + public void addFailure(Test test, AssertionFailedError failure) { + lastMarked = test; + logResult(test, "FAIL"); + out.println(failure.getMessage()); + } + + @Override + public void endTest(Test test) { + if (test != lastMarked) 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 ba66649b..b8cd442a 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -695,4 +695,10 @@ public class ConfigurationKeys { */ public static final ConfigurationKey<CheckerFrameworkVersion> CHECKER_FRAMEWORK = new ConfigurationKey<CheckerFrameworkVersion>("checkerframework", "If set with the version of checkerframework.org (in major.minor, or just 'true' for the latest supported version), create relevant checkerframework.org annotations for code lombok generates (default: false).") {}; + /** + * lombok configuration: {@code lombok.standardException.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @StandardException} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> STANDARD_EXCEPTION_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.standardException.flagUsage", "Emit a warning or error if @StandardException is used.") {}; } diff --git a/src/core/lombok/bytecode/AsmUtil.java b/src/core/lombok/bytecode/AsmUtil.java index 42bf9700..fc2533e5 100644 --- a/src/core/lombok/bytecode/AsmUtil.java +++ b/src/core/lombok/bytecode/AsmUtil.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 @@ -37,7 +37,7 @@ class AsmUtil { ClassReader reader = new ClassReader(byteCode); ClassWriter writer = new FixedClassWriter(reader, 0); - ClassVisitor visitor = new ClassVisitor(Opcodes.ASM7, writer) { + ClassVisitor visitor = new ClassVisitor(Opcodes.ASM9, writer) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return new JSRInlinerAdapter(super.visitMethod(access, name, desc, signature, exceptions), access, name, desc, signature, exceptions); } 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..8ae7af5b 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) { @@ -50,7 +50,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation { class PreventNullAnalysisVisitor extends MethodVisitor { PreventNullAnalysisVisitor(MethodVisitor mv) { - super(Opcodes.ASM7, mv); + super(Opcodes.ASM9, mv); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { @@ -68,7 +68,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation { } } - reader.accept(new ClassVisitor(Opcodes.ASM7, writer) { + reader.accept(new ClassVisitor(Opcodes.ASM9, writer) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return new PreventNullAnalysisVisitor(super.visitMethod(access, name, desc, signature, exceptions)); } diff --git a/src/core/lombok/bytecode/SneakyThrowsRemover.java b/src/core/lombok/bytecode/SneakyThrowsRemover.java index 037cb109..ea1c3cec 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) { @@ -52,7 +52,7 @@ public class SneakyThrowsRemover implements PostCompilerTransformation { class SneakyThrowsRemoverVisitor extends MethodVisitor { SneakyThrowsRemoverVisitor(MethodVisitor mv) { - super(Opcodes.ASM7, mv); + super(Opcodes.ASM9, mv); } private boolean methodInsnQueued = false; @@ -177,7 +177,7 @@ public class SneakyThrowsRemover implements PostCompilerTransformation { } } - reader.accept(new ClassVisitor(Opcodes.ASM7, writer) { + reader.accept(new ClassVisitor(Opcodes.ASM9, writer) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return new SneakyThrowsRemoverVisitor(super.visitMethod(access, name, desc, signature, exceptions)); } diff --git a/src/core/lombok/core/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java index 3fef46b8..28d16298 100644 --- a/src/core/lombok/core/AnnotationProcessor.java +++ b/src/core/lombok/core/AnnotationProcessor.java @@ -219,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)); + } } } diff --git a/src/core/lombok/core/LombokNode.java b/src/core/lombok/core/LombokNode.java index abfc66a6..34273a86 100644 --- a/src/core/lombok/core/LombokNode.java +++ b/src/core/lombok/core/LombokNode.java @@ -160,18 +160,16 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, List<L> fields = new ArrayList<L>(); for (L potentialField : type.down()) { if (potentialField.getKind() != Kind.FIELD) continue; - if (fieldContainsAnnotation(potentialField.get(), get())) fields.add(potentialField); + for (L child : potentialField.down()) { + if (child.getKind() != Kind.ANNOTATION) continue; + if (child.get() == get()) fields.add(potentialField); + } } return fields; } /** - * Return {@code true} if the annotation is attached to the field. - */ - protected abstract boolean fieldContainsAnnotation(N field, N annotation); - - /** * Returns the direct parent node in the AST tree of this node. For example, a local variable declaration's * direct parent can be e.g. an If block, but its {@code up()} {@code LombokNode} is the {@code Method} that contains it. */ diff --git a/src/core/lombok/core/Main.java b/src/core/lombok/core/Main.java index 47afaefd..7be9e2ea 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"; @@ -145,7 +145,7 @@ public class Main { out.println("------------------------------"); } out.println("projectlombok.org " + Version.getFullVersion()); - out.println("Copyright (C) 2009-2018 The Project Lombok Authors."); + out.println("Copyright (C) 2009-2021 The Project Lombok Authors."); out.println("Run 'lombok license' to see the lombok license agreement."); out.println(); out.println("Run lombok without any parameters to start the graphical installer."); 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 af0941bb..eb8ed6e7 100644 --- a/src/core/lombok/core/Version.java +++ b/src/core/lombok/core/Version.java @@ -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.19"; + private static final String VERSION = "1.18.21"; 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/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 14c2d66c..19cc5a9e 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -325,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", 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/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java index 73114c6c..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 @@ -23,7 +23,6 @@ package lombok.eclipse; 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; @@ -31,10 +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 lombok.eclipse.handlers.EclipseHandlerUtil; +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import lombok.permit.Permit; import org.eclipse.core.resources.ResourcesPlugin; @@ -117,7 +115,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { try { return EclipseWorkspaceBasedFileResolver.resolve(fileName); } catch (IllegalArgumentException e) { - EclipseHandlerUtil.warning("Finding 'lombok.config' file failed for '" + fileName + "'", e); + warning("Finding 'lombok.config' file failed for '" + fileName + "'", e); // String msg = e.getMessage(); // if (msg != null && msg.startsWith("Path must include project and resource name")) { // // We shouldn't throw an exception at all, but we can't reproduce this so we need help from our users to figure this out. @@ -347,7 +345,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { case TYPE: return buildType((TypeDeclaration) node); case FIELD: - return buildField((FieldDeclaration) node); + return buildField((FieldDeclaration) node, null); case INITIALIZER: return buildInitializer((Initializer) node); case METHOD: @@ -386,16 +384,18 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { private EclipseNode buildType(TypeDeclaration type) { if (setAndGetAsHandled(type)) return null; List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); - childNodes.addAll(buildFields(type.fields)); + childNodes.addAll(buildFields(type.fields, getRecordFieldAnnotations(type))); childNodes.addAll(buildTypes(type.memberTypes)); childNodes.addAll(buildMethods(type.methods)); childNodes.addAll(buildAnnotations(type.annotations, false)); return putInMap(new EclipseNode(this, type, childNodes, Kind.TYPE)); } - private Collection<EclipseNode> buildFields(FieldDeclaration[] children) { + private Collection<EclipseNode> buildFields(FieldDeclaration[] children, Annotation[][] annotations) { List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); - if (children != null) for (FieldDeclaration child : children) addIfNotNull(childNodes, buildField(child)); + if (children != null) for (int i = 0; i < children.length; i++) { + addIfNotNull(childNodes, buildField(children[i], annotations[i])); + } return childNodes; } @@ -405,13 +405,13 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { return list; } - private EclipseNode buildField(FieldDeclaration field) { - if (field instanceof Initializer) return buildInitializer((Initializer)field); + private EclipseNode buildField(FieldDeclaration field, Annotation[] annotations) { + if (field instanceof Initializer) return buildInitializer((Initializer) field); if (setAndGetAsHandled(field)) return null; List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); addIfNotNull(childNodes, buildTypeUse(field.type)); addIfNotNull(childNodes, buildStatement(field.initialization)); - childNodes.addAll(buildAnnotations(field.annotations, true)); + childNodes.addAll(buildAnnotations(annotations != null ? annotations : field.annotations, true)); return putInMap(new EclipseNode(this, field, childNodes, Kind.FIELD)); } diff --git a/src/core/lombok/eclipse/EclipseNode.java b/src/core/lombok/eclipse/EclipseNode.java index 12e9ccdb..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 @@ -143,16 +143,6 @@ public class EclipseNode extends lombok.core.LombokNode<EclipseAST, EclipseNode, } } - @Override protected boolean fieldContainsAnnotation(ASTNode field, ASTNode annotation) { - if (!(field instanceof FieldDeclaration)) return false; - FieldDeclaration f = (FieldDeclaration) field; - if (f.annotations == null) return false; - for (Annotation childAnnotation : f.annotations) { - if (childAnnotation == annotation) return true; - } - return false; - } - /** {@inheritDoc} */ @Override public String getName() { final char[] n; diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 68ba075b..70d98cc6 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 @@ -337,6 +337,7 @@ public class EclipseHandlerUtil { public static final Field TYPE_REFERENCE__ANNOTATIONS; public static final Class<?> INTERSECTION_BINDING1, INTERSECTION_BINDING2; public static final Field INTERSECTION_BINDING_TYPES1, INTERSECTION_BINDING_TYPES2; + public static final Field TYPE_DECLARATION_RECORD_COMPONENTS; static { STRING_LITERAL__LINE_NUMBER = getField(StringLiteral.class, "lineNumber"); ANNOTATION__MEMBER_VALUE_PAIR_NAME = getField(Annotation.class, "memberValuePairName"); @@ -345,6 +346,7 @@ public class EclipseHandlerUtil { INTERSECTION_BINDING2 = getClass("org.eclipse.jdt.internal.compiler.lookup.IntersectionCastTypeBinding"); INTERSECTION_BINDING_TYPES1 = INTERSECTION_BINDING1 == null ? null : getField(INTERSECTION_BINDING1, "intersectingTypes"); INTERSECTION_BINDING_TYPES2 = INTERSECTION_BINDING2 == null ? null : getField(INTERSECTION_BINDING2, "intersectingTypes"); + TYPE_DECLARATION_RECORD_COMPONENTS = getField(TypeDeclaration.class, "recordComponents"); } public static int reflectInt(Field f, Object o) { @@ -776,13 +778,10 @@ public class EclipseHandlerUtil { } public static boolean hasNonNullAnnotations(EclipseNode node) { - AbstractVariableDeclaration avd = (AbstractVariableDeclaration) node.get(); - if (avd.annotations == null) return false; - for (Annotation annotation : avd.annotations) { - TypeReference typeRef = annotation.type; - if (typeRef != null && typeRef.getTypeName() != null) { - for (String bn : NONNULL_ANNOTATIONS) if (typeMatches(bn, node, typeRef)) return true; - } + for (EclipseNode child : node.down()) { + 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; } @@ -1877,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; } } @@ -2626,6 +2622,73 @@ public class EclipseHandlerUtil { return generateQualifiedTypeRef(source, fromQualifiedName(typeName)); } + /** + * 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); + } + + /** + * 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 & AccRecord) != 0; + } + + /** + * 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; + } + + /** + * 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 AbstractVariableDeclaration[] getRecordComponents(TypeDeclaration typeDeclaration) { + if (typeDeclaration == null || (typeDeclaration.modifiers & AccRecord) == 0) return null; + try { + return (AbstractVariableDeclaration[]) TYPE_DECLARATION_RECORD_COMPONENTS.get(typeDeclaration); + } catch (Exception e) { + // This presumably means this isn't a JDK16 - fall through. + } + return null; + } + + public static Annotation[][] getRecordFieldAnnotations(TypeDeclaration typeDeclaration) { + if (typeDeclaration.fields == null) return null; + Annotation[][] annotations = new Annotation[typeDeclaration.fields.length][]; + + AbstractVariableDeclaration[] recordComponents = getRecordComponents(typeDeclaration); + if (recordComponents != null) { + int j = 0; + for (int i = 0; i < typeDeclaration.fields.length; i++) { + 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) { diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index 760f5282..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 @@ -451,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 189c5a69..c002c4ca 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 @@ -74,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; @@ -98,8 +97,9 @@ 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(); @@ -183,14 +183,14 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { builderMethodName = ann.builderMethodName(); buildMethodName = ann.buildMethodName(); - setBuilderClassName(fixBuilderClassName(node, ann.builderClassName())); + setBuilderClassName(getBuilderClassNameTemplate(node, ann.builderClassName())); toBuilder = ann.toBuilder(); if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; } - static String fixBuilderClassName(EclipseNode node, String override) { + static String getBuilderClassNameTemplate(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; @@ -202,8 +202,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } String replaceBuilderClassName(char[] name) { - if (builderClassName.indexOf('*') == -1) return builderClassName; - return builderClassName.replace("*", new String(name)); + return replaceBuilderClassName(name, builderClassName); + } + + String replaceBuilderClassName(char[] name, String template) { + if (template.indexOf('*') == -1) return template; + return template.replace("*", new String(name)); } String replaceBuilderClassName(String name) { @@ -253,6 +257,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } @Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode 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"); BuilderJob job = new BuilderJob(); job.sourceNode = annotationNode; @@ -288,6 +294,11 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { List<EclipseNode> nonFinalNonDefaultedFields = null; if (parent.get() instanceof TypeDeclaration) { + if (!isClass(parent) && !isRecord(parent)) { + annotationNode.addError(BUILDER_NODE_NOT_SUPPORTED_ERR); + return; + } + job.parentType = parent; TypeDeclaration td = (TypeDeclaration) parent.get(); @@ -336,8 +347,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { allFields.add(fieldNode); } - handleConstructor.generateConstructor(parent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, - Collections.<Annotation>emptyList(), annotationNode); + 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); + } job.typeParams = job.builderTypeParams = td.typeParameters; buildMethodReturnType = job.createBuilderParentTypeReference(); @@ -446,7 +461,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { 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; } 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 3233a8c6..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(); @@ -194,12 +194,7 @@ public class HandleConstructor { } static boolean checkLegality(EclipseNode typeNode, EclipseNode errorNode, String name) { - TypeDeclaration typeDecl = null; - if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0; - - if (typeDecl == null || notAClass) { + if (!isClassOrEnum(typeNode)) { errorNode.addError(name + " is only supported on a class or an enum."); return false; } diff --git a/src/core/lombok/eclipse/handlers/HandleData.java b/src/core/lombok/eclipse/handlers/HandleData.java index 4011890d..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,6 +22,7 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.*; +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.util.Collections; @@ -32,16 +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.eclipse.jdt.internal.compiler.ast.TypeDeclaration; -import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -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(); @@ -55,13 +54,7 @@ public class HandleData extends EclipseAnnotationHandler<Data> { Data ann = annotation.getInstance(); EclipseNode typeNode = annotationNode.up(); - TypeDeclaration typeDecl = null; - if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; - - if (typeDecl == null || notAClass) { + if (!isClass(typeNode)) { annotationNode.addError("@Data is only supported on a class."); return; } 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 deb19c00..12a3c315 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 @@ -48,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; @@ -89,12 +90,11 @@ 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"; @@ -144,18 +144,12 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<Included<EclipseNode, EqualsAndHashCode.Include>> members, Boolean callSuper, boolean whineIfExists, boolean cacheHashCode, FieldAccess fieldAccess, List<Annotation> onParam) { - TypeDeclaration typeDecl = null; - - if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; - - if (typeDecl == null || notAClass) { + 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) { @@ -168,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); @@ -220,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); @@ -256,6 +250,8 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH setGeneratedBy(hashCodeCacheDecl.type, source); } + private static final char[] HASH_CODE = "hashCode".toCharArray(), FLOAT_TO_INT_BITS = "floatToIntBits".toCharArray(), DOUBLE_TO_LONG_BITS = "doubleToLongBits".toCharArray(); + 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; @@ -379,7 +375,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH floatToIntBits.sourceStart = pS; floatToIntBits.sourceEnd = pE; setGeneratedBy(floatToIntBits, source); floatToIntBits.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_FLOAT); - floatToIntBits.selector = "floatToIntBits".toCharArray(); + floatToIntBits.selector = FLOAT_TO_INT_BITS; floatToIntBits.arguments = new Expression[] { fieldAccessor }; statements.add(createResultCalculation(source, floatToIntBits)); } else if (Arrays.equals(TypeConstants.DOUBLE, token)) { @@ -388,7 +384,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH doubleToLongBits.sourceStart = pS; doubleToLongBits.sourceEnd = pE; setGeneratedBy(doubleToLongBits, source); doubleToLongBits.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_DOUBLE); - doubleToLongBits.selector = "doubleToLongBits".toCharArray(); + doubleToLongBits.selector = DOUBLE_TO_LONG_BITS; doubleToLongBits.arguments = new Expression[] { fieldAccessor }; statements.add(createLocalDeclaration(source, dollarFieldName, TypeReference.baseTypeReference(TypeIds.T_long, 0), doubleToLongBits)); SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p); @@ -412,7 +408,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH hashCodeCall.sourceStart = pS; hashCodeCall.sourceEnd = pE; setGeneratedBy(hashCodeCall, source); hashCodeCall.receiver = copy1; - hashCodeCall.selector = "hashCode".toCharArray(); + hashCodeCall.selector = HASH_CODE; NullLiteral nullLiteral = new NullLiteral(pS, pE); setGeneratedBy(nullLiteral, source); EqualExpression objIsNull = new EqualExpression(copy2, nullLiteral, OperatorIds.EQUAL_EQUAL); 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 702713fe..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) { @@ -63,13 +63,7 @@ public class HandleFieldDefaults extends EclipseASTAdapter { } } - TypeDeclaration typeDecl = null; - if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0; - - if (typeDecl == null || notAClass) { + if (!isClassOrEnum(typeNode)) { pos.addError("@FieldDefaults is only supported on a class or an enum."); return false; } 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 4d7d84ce..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) { @@ -90,14 +91,8 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> { } } - TypeDeclaration typeDecl = null; - if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0; - - if (typeDecl == null || notAClass) { - pos.addError("@Getter is only supported on a class, an enum, or a field."); + if (!isClassOrEnum(typeNode)) { + pos.addError(GETTER_NODE_NOT_SUPPORTED_ERR); return false; } @@ -171,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; } 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 8297426d..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 @@ -38,7 +38,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.ConfigurationKeys; import lombok.core.AnnotationValues; @@ -49,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(); } @@ -82,10 +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."); @@ -203,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"); @@ -214,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"); @@ -225,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"); @@ -236,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"); @@ -247,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"); @@ -258,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"); @@ -269,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"); @@ -280,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"); @@ -291,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 903d098b..365eef33 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 @@ -22,10 +22,12 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.handleFlagUsage; -import static lombok.eclipse.Eclipse.isPrimitive; +import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -35,31 +37,40 @@ import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.AssertStatement; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.Block; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.EqualExpression; +import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; +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.MessageSend; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; 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.SynchronizedStatement; +import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.ThrowStatement; import org.eclipse.jdt.internal.compiler.ast.TryStatement; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; -import org.mangosdk.spi.ProviderFor; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import lombok.ConfigurationKeys; import lombok.NonNull; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; -import lombok.eclipse.DeferUntilPostDiet; +import lombok.eclipse.EcjAugments; import lombok.eclipse.EclipseAST; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.spi.Provides; -@DeferUntilPostDiet -@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(); @@ -80,10 +91,163 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> { } } + private List<FieldDeclaration> getRecordComponents(EclipseNode typeNode) { + List<FieldDeclaration> list = new ArrayList<FieldDeclaration>(); + + for (EclipseNode child : typeNode.down()) { + if (child.getKind() == Kind.FIELD) { + FieldDeclaration fd = (FieldDeclaration) child.get(); + if ((fd.modifiers & AccRecord) != 0) list.add(fd); + } + } + + return list; + } + + private EclipseNode addCompactConstructorIfNeeded(EclipseNode typeNode, EclipseNode annotationNode) { + // explicit Compact Constructor has bits set: Bit32, IsCanonicalConstructor (10). + // implicit Compact Constructor has bits set: Bit32, IsCanonicalConstructor (10), and IsImplicit (11). + // explicit constructor with long-form shows up as a normal constructor (Bit32 set, that's all), but the + // implicit CC is then also present and will presumably be stripped out in some later phase. + + EclipseNode toRemove = null; + EclipseNode existingCompactConstructor = null; + List<FieldDeclaration> recordComponents = null; + for (EclipseNode child : typeNode.down()) { + if (!(child.get() instanceof ConstructorDeclaration)) continue; + ConstructorDeclaration cd = (ConstructorDeclaration) child.get(); + if ((cd.bits & IsCanonicalConstructor) != 0) { + if ((cd.bits & IsImplicit) != 0) { + toRemove = child; + } else { + existingCompactConstructor = child; + } + } else { + // If this constructor has exact matching types vs. the record components, + // this is the canonical constructor in long form and we should not generate one. + + if (recordComponents == null) recordComponents = getRecordComponents(typeNode); + int argLength = cd.arguments == null ? 0 : cd.arguments.length; + int compLength = recordComponents.size(); + boolean isCanonical = argLength == compLength; + if (isCanonical) top: for (int i = 0; i < argLength; i++) { + TypeReference a = recordComponents.get(i).type; + TypeReference b = cd.arguments[i] == null ? null : cd.arguments[i].type; + // technically this won't match e.g. `java.lang.String` to just `String`; + // to use this feature you'll need to use the same way to write it, which seems + // like a fair requirement. + char[][] ta = getRawTypeName(a); + char[][] tb = getRawTypeName(b); + if (ta == null || tb == null || ta.length != tb.length) { + isCanonical = false; + break top; + } + for (int j = 0; j < ta.length; j++) { + if (!Arrays.equals(ta[j], tb[j])) { + isCanonical = false; + break top; + } + } + } + if (isCanonical) { + return null; + } + } + } + if (existingCompactConstructor != null) return existingCompactConstructor; + int posToInsert = -1; + TypeDeclaration td = (TypeDeclaration) typeNode.get(); + if (toRemove != null) { + int idxToRemove = -1; + for (int i = 0; i < td.methods.length; i++) { + if (td.methods[i] == toRemove.get()) idxToRemove = i; + } + if (idxToRemove != -1) { + System.arraycopy(td.methods, idxToRemove + 1, td.methods, idxToRemove, td.methods.length - idxToRemove - 1); + posToInsert = td.methods.length - 1; + typeNode.removeChild(toRemove); + } + } + if (posToInsert == -1) { + AbstractMethodDeclaration[] na = new AbstractMethodDeclaration[td.methods.length + 1]; + posToInsert = td.methods.length; + System.arraycopy(td.methods, 0, na, 0, posToInsert); + td.methods = na; + } + + ConstructorDeclaration cd = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); + cd.modifiers = ClassFileConstants.AccPublic; + cd.bits = ASTNode.Bit32 | ECLIPSE_DO_NOT_TOUCH_FLAG | IsCanonicalConstructor; + cd.selector = td.name; + cd.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); + if (recordComponents == null) recordComponents = getRecordComponents(typeNode); + cd.arguments = new Argument[recordComponents.size()]; + cd.statements = new Statement[recordComponents.size()]; + cd.bits = IsCanonicalConstructor; + + for (int i = 0; i < cd.arguments.length; i++) { + FieldDeclaration cmp = recordComponents.get(i); + cd.arguments[i] = new Argument(cmp.name, cmp.sourceStart, cmp.type, 0); + cd.arguments[i].bits = ASTNode.IsArgument | ASTNode.IgnoreRawTypeCheck | ASTNode.IsReachable; + FieldReference lhs = new FieldReference(cmp.name, 0); + lhs.receiver = new ThisReference(0, 0); + SingleNameReference rhs = new SingleNameReference(cmp.name, 0); + cd.statements[i] = new Assignment(lhs, rhs, cmp.sourceEnd); + } + + setGeneratedBy(cd, annotationNode.get()); + for (int i = 0; i < cd.arguments.length; i++) { + FieldDeclaration cmp = recordComponents.get(i); + cd.arguments[i].sourceStart = cmp.sourceStart; + cd.arguments[i].sourceEnd = cmp.sourceStart; + cd.arguments[i].declarationSourceEnd = cmp.sourceStart; + cd.arguments[i].declarationEnd = cmp.sourceStart; + } + + td.methods[posToInsert] = cd; + cd.annotations = addSuppressWarningsAll(typeNode, cd, cd.annotations); + cd.annotations = addGenerated(typeNode, cd, cd.annotations); + return typeNode.add(cd, Kind.METHOD); + } + + private static char[][] getRawTypeName(TypeReference a) { + if (a instanceof QualifiedTypeReference) return ((QualifiedTypeReference) a).tokens; + if (a instanceof SingleTypeReference) return new char[][] {((SingleTypeReference) a).token}; + return null; + } + @Override public void handle(AnnotationValues<NonNull> annotation, Annotation ast, EclipseNode annotationNode) { + // Generating new methods is only possible during diet parse but modifying existing methods requires a full parse. + // As we need both for @NonNull we reset the handled flag during diet parse. + + if (!annotationNode.isCompleteParse()) { + if (annotationNode.up().getKind() == Kind.FIELD) { + //Check if this is a record and we need to generate the compact form constructor. + EclipseNode typeNode = annotationNode.up().up(); + if (typeNode.getKind() == Kind.TYPE) { + if (isRecord(typeNode)) { + addCompactConstructorIfNeeded(typeNode, annotationNode); + } + } + } + + EcjAugments.ASTNode_handled.clear(ast); + return; + } + handle0(ast, annotationNode, false); } + private EclipseNode findCompactConstructor(EclipseNode typeNode) { + for (EclipseNode child : typeNode.down()) { + if (!(child.get() instanceof ConstructorDeclaration)) continue; + ConstructorDeclaration cd = (ConstructorDeclaration) child.get(); + if ((cd.bits & IsCanonicalConstructor) != 0 && (cd.bits & IsImplicit) == 0) return child; + } + + return null; + } + private void handle0(Annotation ast, EclipseNode annotationNode, boolean force) { handleFlagUsage(annotationNode, ConfigurationKeys.NON_NULL_FLAG_USAGE, "@NonNull"); @@ -92,13 +256,26 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> { // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning // behaviour on _OUR_ 'lombok.NonNull'. + EclipseNode fieldNode = annotationNode.up(); + EclipseNode typeNode = fieldNode.up(); try { if (isPrimitive(((AbstractVariableDeclaration) annotationNode.up().get()).type)) { annotationNode.addWarning("@NonNull is meaningless on a primitive."); + return; } } catch (Exception ignore) {} + if (isRecord(typeNode)) { + // well, these kinda double as parameters (of the compact constructor), so we do some work here. + // NB:Tthe diet parse run already added an explicit compact constructor if we need to take any actions. + EclipseNode compactConstructor = findCompactConstructor(typeNode); + + if (compactConstructor != null) { + addNullCheckIfNeeded((AbstractMethodDeclaration) compactConstructor.get(), (AbstractVariableDeclaration) fieldNode.get(), annotationNode); + } + } + return; } @@ -140,6 +317,11 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> { return; } + addNullCheckIfNeeded(declaration, param, annotationNode); + paramNode.up().rebuild(); + } + + private void addNullCheckIfNeeded(AbstractMethodDeclaration declaration, AbstractVariableDeclaration param, EclipseNode annotationNode) { // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter, // 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. @@ -188,7 +370,6 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> { newStatements[skipOver] = nullCheck; declaration.statements = newStatements; } - paramNode.up().rebuild(); } public boolean isNullCheck(Statement stat) { 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 83c6eef2..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)) { @@ -71,14 +73,8 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { } } - TypeDeclaration typeDecl = null; - if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; - - if (typeDecl == null || notAClass) { - pos.addError("@Setter is only supported on a class or a field."); + if (!isClass(typeNode)) { + pos.addError(SETTER_NODE_NOT_SUPPORTED_ERR); return false; } @@ -148,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; } 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/HandleStandardException.java b/src/core/lombok/eclipse/handlers/HandleStandardException.java new file mode 100755 index 00000000..def6e495 --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleStandardException.java @@ -0,0 +1,253 @@ +/* + * 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.eclipse.handlers; + +import lombok.AccessLevel; +import lombok.ConfigurationKeys; +import lombok.experimental.StandardException; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.EclipseHandlerUtil.*; +import lombok.spi.Provides; +import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; + +import java.lang.reflect.Modifier; +import java.util.*; + +import static lombok.core.handlers.HandlerUtil.handleFlagUsage; +import static lombok.eclipse.Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; + +@Provides +public class HandleStandardException extends EclipseAnnotationHandler<StandardException> { + @Override + public void handle(AnnotationValues<StandardException> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.STANDARD_EXCEPTION_FLAG_USAGE, "@StandardException"); + + EclipseNode typeNode = annotationNode.up(); + if (!isClass(typeNode)) { + annotationNode.addError("@StandardException is only supported on a class"); + return; + } + + TypeDeclaration classDef = (TypeDeclaration) typeNode.get(); + if (classDef.superclass == null) { + annotationNode.addError("@StandardException requires that you extend a Throwable type"); + return; + } + + AccessLevel access = annotation.getInstance().access(); + + generateNoArgsConstructor(typeNode, access, annotationNode); + generateMsgOnlyConstructor(typeNode, access, annotationNode); + generateCauseOnlyConstructor(typeNode, access, annotationNode); + generateFullConstructor(typeNode, access, annotationNode); + } + + private void generateNoArgsConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) { + if (hasConstructor(typeNode) != MemberExistsResult.NOT_EXISTS) return; + int pS = source.get().sourceStart, pE = source.get().sourceEnd; + + ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This); + explicitCall.arguments = new Expression[] {new NullLiteral(pS, pE), new NullLiteral(pS, pE)}; + ConstructorDeclaration constructor = createConstructor(level, typeNode, false, false, source, explicitCall, null); + injectMethod(typeNode, constructor); + } + + private void generateMsgOnlyConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) { + if (hasConstructor(typeNode, String.class) != MemberExistsResult.NOT_EXISTS) return; + int pS = source.get().sourceStart, pE = source.get().sourceEnd; + long p = (long) pS << 32 | pE; + + ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This); + explicitCall.arguments = new Expression[] {new SingleNameReference(MESSAGE, p), new NullLiteral(pS, pE)}; + ConstructorDeclaration constructor = createConstructor(level, typeNode, true, false, source, explicitCall, null); + injectMethod(typeNode, constructor); + } + + private void generateCauseOnlyConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) { + if (hasConstructor(typeNode, Throwable.class) != MemberExistsResult.NOT_EXISTS) return; + int pS = source.get().sourceStart, pE = source.get().sourceEnd; + long p = (long) pS << 32 | pE; + + ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.This); + Expression causeNotNull = new EqualExpression(new SingleNameReference(CAUSE, p), new NullLiteral(pS, pE), OperatorIds.NOT_EQUAL); + MessageSend causeDotGetMessage = new MessageSend(); + causeDotGetMessage.sourceStart = pS; causeDotGetMessage.sourceEnd = pE; + causeDotGetMessage.receiver = new SingleNameReference(CAUSE, p); + causeDotGetMessage.selector = GET_MESSAGE; + Expression messageExpr = new ConditionalExpression(causeNotNull, causeDotGetMessage, new NullLiteral(pS, pE)); + explicitCall.arguments = new Expression[] {messageExpr, new SingleNameReference(CAUSE, p)}; + ConstructorDeclaration constructor = createConstructor(level, typeNode, false, true, source, explicitCall, null); + injectMethod(typeNode, constructor); + } + + private void generateFullConstructor(EclipseNode typeNode, AccessLevel level, EclipseNode source) { + if (hasConstructor(typeNode, String.class, Throwable.class) != MemberExistsResult.NOT_EXISTS) return; + int pS = source.get().sourceStart, pE = source.get().sourceEnd; + long p = (long) pS << 32 | pE; + + ExplicitConstructorCall explicitCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); + explicitCall.arguments = new Expression[] {new SingleNameReference(MESSAGE, p)}; + Expression causeNotNull = new EqualExpression(new SingleNameReference(CAUSE, p), new NullLiteral(pS, pE), OperatorIds.NOT_EQUAL); + MessageSend causeDotInitCause = new MessageSend(); + causeDotInitCause.sourceStart = pS; causeDotInitCause.sourceEnd = pE; + causeDotInitCause.receiver = new SuperReference(pS, pE); + causeDotInitCause.selector = INIT_CAUSE; + causeDotInitCause.arguments = new Expression[] {new SingleNameReference(CAUSE, p)}; + IfStatement ifs = new IfStatement(causeNotNull, causeDotInitCause, pS, pE); + ConstructorDeclaration constructor = createConstructor(level, typeNode, true, true, source, explicitCall, ifs); + injectMethod(typeNode, constructor); + } + + /** + * Checks if a constructor with the provided parameters exists under the type node. + */ + public static MemberExistsResult hasConstructor(EclipseNode node, Class<?>... paramTypes) { + node = upToTypeNode(node); + + if (node != null && node.get() instanceof TypeDeclaration) { + 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 (!paramsMatch(node, def.arguments, paramTypes)) continue; + return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; + } + } + } + + return MemberExistsResult.NOT_EXISTS; + } + + private static boolean paramsMatch(EclipseNode node, Argument[] a, Class<?>[] b) { + if (a == null) return b == null || b.length == 0; + if (b == null) return a.length == 0; + if (a.length != b.length) return false; + + for (int i = 0; i < a.length; i++) { + if (!typeMatches(b[i], node, a[i].type)) return false; + } + return true; + } + + private static final char[][] JAVA_BEANS_CONSTRUCTORPROPERTIES = new char[][] { "java".toCharArray(), "beans".toCharArray(), "ConstructorProperties".toCharArray() }; + private static final char[] MESSAGE = "message".toCharArray(), CAUSE = "cause".toCharArray(), GET_MESSAGE = "getMessage".toCharArray(), INIT_CAUSE = "initCause".toCharArray(); + + public static Annotation[] createConstructorProperties(ASTNode source, boolean msgParam, boolean causeParam) { + if (!msgParam && !causeParam) return null; + + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long) pS << 32 | pE; + long[] poss = new long[3]; + Arrays.fill(poss, p); + + QualifiedTypeReference constructorPropertiesType = new QualifiedTypeReference(JAVA_BEANS_CONSTRUCTORPROPERTIES, poss); + setGeneratedBy(constructorPropertiesType, source); + SingleMemberAnnotation ann = new SingleMemberAnnotation(constructorPropertiesType, pS); + ann.declarationSourceEnd = pE; + + ArrayInitializer fieldNames = new ArrayInitializer(); + fieldNames.sourceStart = pS; + fieldNames.sourceEnd = pE; + fieldNames.expressions = new Expression[(msgParam && causeParam) ? 2 : 1]; + + int ctr = 0; + if (msgParam) { + fieldNames.expressions[ctr] = new StringLiteral(MESSAGE, pS, pE, 0); + setGeneratedBy(fieldNames.expressions[ctr], source); + ctr++; + } + if (causeParam) { + fieldNames.expressions[ctr] = new StringLiteral(CAUSE, pS, pE, 0); + setGeneratedBy(fieldNames.expressions[ctr], source); + ctr++; + } + + ann.memberValue = fieldNames; + setGeneratedBy(ann, source); + setGeneratedBy(ann.memberValue, source); + return new Annotation[] { ann }; + } + + @SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor(AccessLevel level, EclipseNode typeNode, boolean msgParam, boolean causeParam, EclipseNode sourceNode, ExplicitConstructorCall explicitCall, Statement extra) { + ASTNode source = sourceNode.get(); + TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get()); + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long) pS << 32 | pE; + + boolean addConstructorProperties; + if ((!msgParam && !causeParam) || isLocalType(typeNode)) { + addConstructorProperties = false; + } else { + Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES); + addConstructorProperties = v != null ? v.booleanValue() : + Boolean.FALSE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + } + + ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); + + constructor.modifiers = toEclipseModifier(level); + constructor.selector = typeDeclaration.name; + constructor.thrownExceptions = null; + constructor.typeParameters = null; + constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = pS; + constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = pE; + constructor.arguments = null; + + List<Argument> params = new ArrayList<Argument>(); + + if (msgParam) { + TypeReference typeRef = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p, p, p}); + Argument parameter = new Argument(MESSAGE, p, typeRef, Modifier.FINAL); + params.add(parameter); + } + if (causeParam) { + TypeReference typeRef = new QualifiedTypeReference(TypeConstants.JAVA_LANG_THROWABLE, new long[] {p, p, p}); + Argument parameter = new Argument(CAUSE, p, typeRef, Modifier.FINAL); + params.add(parameter); + } + + explicitCall.sourceStart = pS; + explicitCall.sourceEnd = pE; + constructor.constructorCall = explicitCall; + constructor.statements = extra != null ? new Statement[] {extra} : null; + constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[0]); + + Annotation[] constructorProperties = null; + if (addConstructorProperties) constructorProperties = createConstructorProperties(source, msgParam, causeParam); + constructor.annotations = copyAnnotations(source, constructorProperties); + constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); + return constructor; + } + + public static boolean isLocalType(EclipseNode type) { + Kind kind = type.up().getKind(); + if (kind == Kind.COMPILATION_UNIT) return false; + if (kind == Kind.TYPE) return isLocalType(type.up()); + return true; + } +} diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 8fdfd315..4d5d2448 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 @@ -50,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; @@ -74,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; @@ -100,8 +100,9 @@ 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[] SELF_METHOD_NAME = "self".toCharArray(); @@ -123,7 +124,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; - builderClassName = fixBuilderClassName(node, ""); + builderClassName = getBuilderClassNameTemplate(node, null); } EclipseNode builderAbstractType; @@ -179,10 +180,11 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { List<EclipseNode> nonFinalNonDefaultedFields = null; - if (!(parent.get() instanceof TypeDeclaration)) { - annotationNode.addError("@SuperBuilder is only supported on types."); + if (!isClass(parent)) { + annotationNode.addError("@SuperBuilder is only supported on classes."); return; } + job.parentType = parent; TypeDeclaration td = (TypeDeclaration) parent.get(); @@ -272,8 +274,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { }; if (extendsClause instanceof QualifiedTypeReference) { QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference)extendsClause; - String superclassClassName = String.valueOf(qualifiedTypeReference.getLastToken()); - String superclassBuilderClassName = job.replaceBuilderClassName(superclassClassName); + char[] superclassClassName = qualifiedTypeReference.getLastToken(); + String builderClassNameTemplate = BuilderJob.getBuilderClassNameTemplate(annotationNode, null); + String superclassBuilderClassName = job.replaceBuilderClassName(superclassClassName, builderClassNameTemplate); char[][] tokens = Arrays.copyOf(qualifiedTypeReference.tokens, qualifiedTypeReference.tokens.length + 1); tokens[tokens.length-1] = superclassBuilderClassName.toCharArray(); @@ -289,10 +292,11 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss); } else if (extendsClause != null) { - String superClass = String.valueOf(extendsClause.getTypeName()[0]); - String superclassBuilderClassName = superClass + "Builder"; + char[] superclassClassName = extendsClause.getTypeName()[0]; + String builderClassNameTemplate = BuilderJob.getBuilderClassNameTemplate(annotationNode, null); + String superclassBuilderClassName = job.replaceBuilderClassName(superclassClassName, builderClassNameTemplate); - char[][] tokens = new char[][] {superClass.toCharArray(), superclassBuilderClassName.toCharArray()}; + char[][] tokens = new char[][] {superclassClassName, superclassBuilderClassName.toCharArray()}; long[] poss = new long[tokens.length]; Arrays.fill(poss, job.getPos()); @@ -606,6 +610,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } 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(job.source), typeDeclaration.scope); @@ -635,6 +642,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { AllocationExpression invoke = new AllocationExpression(); 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)}; + } createRelevantNonNullAnnotation(job.parentType, out); out.traverse(new SetGeneratedByVisitor(job.source), ((TypeDeclaration) job.parentType.get()).scope); @@ -673,6 +683,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { 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)}; + } createRelevantNonNullAnnotation(job.parentType, out); out.traverse(new SetGeneratedByVisitor(job.source), ((TypeDeclaration) job.parentType.get()).scope); @@ -807,9 +820,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); @@ -1093,6 +1104,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])); @@ -1195,7 +1207,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 08d00d91..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> { @@ -148,6 +148,11 @@ public class HandleSynchronized extends EclipseAnnotationHandler<Synchronized> { return; } + EclipseNode typeNode = upToTypeNode(annotationNode); + if (!isClassOrEnum(typeNode)) { + 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; @@ -163,7 +168,6 @@ public class HandleSynchronized extends EclipseAnnotationHandler<Synchronized> { Expression lockVariable; if (isStatic[0]) { - EclipseNode typeNode = upToTypeNode(annotationNode); char[][] n = getQualifiedInnerName(typeNode, lockName); long[] ps = new long[n.length]; Arrays.fill(ps, pos); diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index a6bcb24f..171402b3 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 @@ -50,9 +50,7 @@ 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.ast.TypeReference; -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; @@ -67,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"); @@ -116,14 +115,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> { public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List<Included<EclipseNode, ToString.Include>> members, boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) { - TypeDeclaration typeDecl = null; - - if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0; - - if (typeDecl == null || notAClass) { + if (!isClassOrEnum(typeNode)) { errorNode.addError("@ToString is only supported on a class or enum."); return; } @@ -169,12 +161,12 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> { String typeName = getTypeName(type); boolean isEnum = type.isEnumType(); - + char[] suffix = ")".toCharArray(); String infixS = ", "; char[] infix = infixS.toCharArray(); int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long)pS << 32 | pE; + long p = (long) pS << 32 | pE; final int PLUS = OperatorIds.PLUS; String prefix; diff --git a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java index 2349f839..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"); @@ -70,13 +68,8 @@ public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> { } private static boolean checkLegality(EclipseNode typeNode, EclipseNode errorNode) { - TypeDeclaration typeDecl = null; - if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; - - if (typeDecl == null || notAClass) { - errorNode.addError("@UtilityClass is only supported on a class (can't be an interface, enum, or annotation)."); + if (!isClass(typeNode)) { + errorNode.addError("@UtilityClass is only supported on a class."); return false; } @@ -158,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 2e0338a8..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,16 +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(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; - - if (typeDecl == null || notAClass) { + 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 dce193d8..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) { diff --git a/src/core/lombok/eclipse/handlers/HandleWithBy.java b/src/core/lombok/eclipse/handlers/HandleWithBy.java index f56004f6..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) { 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/experimental/StandardException.java b/src/core/lombok/experimental/StandardException.java new file mode 100644 index 00000000..b04ac2ee --- /dev/null +++ b/src/core/lombok/experimental/StandardException.java @@ -0,0 +1,50 @@ +/* + * 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.experimental; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import lombok.AccessLevel; + +/** + * Put on any class that extends some {@code java.lang.Throwable} type to add the 4 common exception constructors. + * + * Specifically, all 4 constructors derived from the combinatorial explosion of {@code String message} and {@code Throwable cause}. + * You may write any or all of these 4 constructors by hand; lombok will only generate the missing ones. + * <p> + * All but the full {@code (String message, Throwable cause)} constructor are implemented as a {@code this(msg, cause)} call; it is therefore + * possibly to write code to run on construction by writing just the {@code (String message, Throwable cause)} constructor. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface StandardException { + /** + * Sets the access level of the generated constuctors. By default, generated constructors are {@code public}. + * Note: This does nothing if you write your own constructors (we won't change their access levels). + * + * @return The constructors will be generated with this access modifier. + */ + AccessLevel access() default lombok.AccessLevel.PUBLIC; +} 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/JavacAugments.java b/src/core/lombok/javac/JavacAugments.java index bc23131b..e5d1fc43 100644 --- a/src/core/lombok/javac/JavacAugments.java +++ b/src/core/lombok/javac/JavacAugments.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 @@ -24,6 +24,7 @@ package lombok.javac; import lombok.core.FieldAugment; import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCImport; public final class JavacAugments { private JavacAugments() { @@ -32,4 +33,5 @@ public final class JavacAugments { public static final FieldAugment<JCTree, Boolean> JCTree_handled = FieldAugment.augment(JCTree.class, boolean.class, "lombok$handled"); public static final FieldAugment<JCTree, JCTree> JCTree_generatedNode = FieldAugment.circularSafeAugment(JCTree.class, JCTree.class, "lombok$generatedNode"); + public static final FieldAugment<JCImport, Boolean> JCImport_deletable = FieldAugment.circularSafeAugment(JCImport.class, Boolean.class, "lombok$deletable"); } diff --git a/src/core/lombok/javac/JavacNode.java b/src/core/lombok/javac/JavacNode.java index 3de3f38b..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 @@ -186,16 +186,6 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre return false; } - @Override protected boolean fieldContainsAnnotation(JCTree field, JCTree annotation) { - if (!(field instanceof JCVariableDecl)) return false; - JCVariableDecl f = (JCVariableDecl) field; - if (f.mods.annotations == null) return false; - for (JCAnnotation childAnnotation : f.mods.annotations) { - if (childAnnotation == annotation) return true; - } - return false; - } - /** * Convenient shortcut to the owning JavacAST object's getTreeMaker method. * diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java index 63ca69dd..e96079e0 100644 --- a/src/core/lombok/javac/JavacResolution.java +++ b/src/core/lombok/javac/JavacResolution.java @@ -71,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); @@ -352,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. @@ -450,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 403b5672..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,7 +34,6 @@ 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; diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java index 6e5e603b..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 @@ -63,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. @@ -430,6 +432,7 @@ 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 @@ -446,7 +449,91 @@ public class LombokProcessor extends AbstractProcessor { "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 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 e867fd63..c23dc14c 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 @@ -30,8 +30,6 @@ import java.util.ArrayList; 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; @@ -81,8 +79,9 @@ 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(); @@ -131,7 +130,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { builderMethodName = ann.builderMethodName(); buildMethodName = ann.buildMethodName(); - builderClassName = fixBuilderClassName(node, ann.builderClassName()); + builderClassName = getBuilderClassNameTemplate(node, ann.builderClassName()); toBuilder = ann.toBuilder(); if (builderMethodName == null) builderMethodName = "builder"; @@ -139,7 +138,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (builderClassName == null) builderClassName = ""; } - static String fixBuilderClassName(JavacNode node, String override) { + static String getBuilderClassNameTemplate(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; @@ -147,8 +146,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } String replaceBuilderClassName(Name name) { - if (builderClassName.indexOf('*') == -1) return builderClassName; - return builderClassName.replace("*", name.toString()); + return replaceBuilderClassName(name.toString(), builderClassName); + } + + String replaceBuilderClassName(String name, String template) { + if (template.indexOf('*') == -1) return template; + return template.replace("*", name); } JCExpression createBuilderParentTypeReference() { @@ -193,6 +196,8 @@ 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"); BuilderJob job = new BuilderJob(); job.sourceNode = annotationNode; @@ -230,6 +235,11 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { ArrayList<JavacNode> nonFinalNonDefaultedFields = null; if (parent.get() instanceof JCClassDecl) { + if (!isClass(parent) && !isRecord(parent)) { + annotationNode.addError(BUILDER_NODE_NOT_SUPPORTED_ERR); + return; + } + job.parentType = parent; JCClassDecl td = (JCClassDecl) parent.get(); @@ -280,7 +290,11 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { allFields.append(fieldNode); } - handleConstructor.generateConstructor(parent, 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); + } buildMethodReturnType = namePlusTypeParamsToTypeReference(parent.getTreeMaker(), parent, td.typarams); job.typeParams = job.builderTypeParams = td.typarams; @@ -388,7 +402,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; } @@ -608,10 +622,6 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { private JCMethodDecl generateToBuilderMethod(BuilderJob job, List<JCTypeParameter> typeParameters, String prefix) { // return new ThingieBuilder<A, B>().setA(this.a).setB(this.b); JavacTreeMaker maker = job.getTreeMaker(); - ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); - for (JCTypeParameter typeParam : typeParameters) { - typeArgs.append(maker.Ident(typeParam.name)); - } 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; @@ -804,13 +814,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { public JCMethodDecl generateBuilderMethod(BuilderJob job) { //String builderClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams, AccessLevel access) { //builderClassName, annotationNode, tdParent, typeParams, accessForOuters); - - JavacTreeMaker maker = job.getTreeMaker(); - ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); - for (JCTypeParameter typeParam : job.typeParams) { - typeArgs.append(maker.Ident(typeParam.name)); - } + JavacTreeMaker maker = job.getTreeMaker(); JCExpression call; if (job.isStatic) { @@ -876,7 +881,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { bfd.createdFields.add(field); } } - for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, job.sourceNode); + for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, job.sourceNode); } public void makePrefixedSetterMethodsForBuilder(BuilderJob job, BuilderFieldData bfd, String prefix) { diff --git a/src/core/lombok/javac/handlers/HandleBuilderDefault.java b/src/core/lombok/javac/handlers/HandleBuilderDefault.java index 54060233..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) { 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 12c3b1e9..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; @@ -59,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"); diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index b582490b..dc70e2ce 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.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; @@ -59,9 +57,10 @@ 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(); @@ -83,7 +82,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(); @@ -133,7 +132,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(); @@ -179,12 +178,7 @@ public class HandleConstructor { } public static boolean checkLegality(JavacNode typeNode, JavacNode errorNode, String name) { - JCClassDecl typeDecl = null; - if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); - long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; - boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION)) != 0; - - if (typeDecl == null || notAClass) { + if (!isClassOrEnum(typeNode)) { errorNode.addError(name + " is only supported on a class or an enum."); return false; } 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 3e3891c9..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; @@ -82,8 +80,9 @@ 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> { diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index e76d701e..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,8 +30,6 @@ 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.JCAnnotation; @@ -70,11 +68,12 @@ 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"; @@ -96,9 +95,9 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS); boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; - + boolean cacheHashCode = ann.cacheStrategy() == CacheStrategy.LAZY; - + generateMethods(typeNode, annotationNode, members, callSuper, true, cacheHashCode, fieldAccess, onParam); } @@ -119,13 +118,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas public void generateMethods(JavacNode typeNode, JavacNode source, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, Boolean callSuper, boolean whineIfExists, boolean cacheHashCode, FieldAccess fieldAccess, List<JCAnnotation> onParam) { - boolean notAClass = true; - if (typeNode.get() instanceof JCClassDecl) { - long flags = ((JCClassDecl) typeNode.get()).mods.flags; - notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0; - } - - if (notAClass) { + if (!isClass(typeNode)) { source.addError("@EqualsAndHashCode is only supported on a class."); return; } @@ -141,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); @@ -193,6 +162,30 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas //fallthrough } + 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); diff --git a/src/core/lombok/javac/handlers/HandleExtensionMethod.java b/src/core/lombok/javac/handlers/HandleExtensionMethod.java index bda8c93a..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 @@ -39,8 +39,7 @@ import lombok.experimental.ExtensionMethod; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacResolution; - -import org.mangosdk.spi.ProviderFor; +import lombok.spi.Provides; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.util.TreeScanner; @@ -65,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 @@ -152,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) { diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java index aa381c0d..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) { @@ -57,12 +56,7 @@ public class HandleFieldDefaults extends JavacASTAdapter { } } - JCClassDecl typeDecl = null; - if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); - long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; - boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION)) != 0; - - if (typeDecl == null || notAClass) { + if (!isClassOrEnum(typeNode)) { errorNode.addError("@FieldDefaults is only supported on a class or an enum."); return false; } diff --git a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java index f8bcfbfc..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"); diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 4b2d01e1..7a7e41f9 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,15 +43,13 @@ 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.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCBinary; 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.JCIf; @@ -70,8 +68,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)) { @@ -80,13 +80,8 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { } } - JCClassDecl typeDecl = null; - if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); - long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; - boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION)) != 0; - - if (typeDecl == null || notAClass) { - errorNode.addError("@Getter is only supported on a class, an enum, or a field."); + if (!isClassOrEnum(typeNode)) { + errorNode.addError(GETTER_NODE_NOT_SUPPORTED_ERR); return; } @@ -170,7 +165,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; } @@ -279,9 +274,9 @@ 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")); + suppressions.append(treeMaker.Literal("unchecked")); addAnnotation(decl.mods, field, source, "java.lang.SuppressWarnings", treeMaker.NewArray(null, List.<JCExpression>nil(), suppressions.toList())); } diff --git a/src/core/lombok/javac/handlers/HandleHelper.java b/src/core/lombok/javac/handlers/HandleHelper.java index bb92090d..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; diff --git a/src/core/lombok/javac/handlers/HandleJacksonized.java b/src/core/lombok/javac/handlers/HandleJacksonized.java index f9809865..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> { diff --git a/src/core/lombok/javac/handlers/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java index 7169d33e..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,12 +31,12 @@ 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.JCAnnotation; @@ -75,9 +75,14 @@ public class HandleLog { return; } + if (isRecord(typeNode) && !useStatic) { + annotationNode.addError("Logger fields must be static in records."); + return; + } + 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."); @@ -119,7 +124,14 @@ public class HandleLog { maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (useStatic ? Flags.STATIC : 0)), typeNode.toName(logFieldName), loggerType, factoryMethodCall), source); - injectFieldAndMarkGenerated(typeNode, fieldDecl); + 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; } @@ -154,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"); @@ -165,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"); @@ -176,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"); @@ -187,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"); @@ -198,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"); @@ -209,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"); @@ -220,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"); @@ -231,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"); @@ -242,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 3dd7e328..786a7659 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,13 +27,16 @@ 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 java.util.ArrayList; +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.JCAssert; import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCBinary; 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; @@ -42,15 +45,19 @@ import com.sun.tools.javac.tree.JCTree.JCIf; import com.sun.tools.javac.tree.JCTree.JCLiteral; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; +import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCParens; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCSynchronized; import com.sun.tools.javac.tree.JCTree.JCThrow; import com.sun.tools.javac.tree.JCTree.JCTry; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; +import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.NonNull; import lombok.core.AST.Kind; @@ -58,68 +65,102 @@ import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; +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> { - @Override public void handle(AnnotationValues<NonNull> annotation, JCAnnotation ast, JavacNode annotationNode) { - handleFlagUsage(annotationNode, ConfigurationKeys.NON_NULL_FLAG_USAGE, "@NonNull"); + private JCMethodDecl createRecordArgslessConstructor(JavacNode typeNode, JavacNode source) { + JavacTreeMaker maker = typeNode.getTreeMaker(); - if (annotationNode.up().getKind() == Kind.FIELD) { - // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc), - // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to - // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning - // behaviour on _OUR_ 'lombok.NonNull'. - - try { - if (isPrimitive(((JCVariableDecl) annotationNode.up().get()).vartype)) { - annotationNode.addWarning("@NonNull is meaningless on a primitive."); + java.util.List<JCVariableDecl> fields = new ArrayList<JCVariableDecl>(); + for (JavacNode child : typeNode.down()) { + if (child.getKind() == Kind.FIELD) { + JCVariableDecl v = (JCVariableDecl) child.get(); + if ((v.mods.flags & RECORD) != 0) { + fields.add(v); } - } catch (Exception ignore) {} - - return; + } } - JCMethodDecl declaration; - JavacNode paramNode; + ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); - switch (annotationNode.up().getKind()) { - case ARGUMENT: - paramNode = annotationNode.up(); - break; - case TYPE_USE: - JavacNode typeNode = annotationNode.directUp(); - paramNode = typeNode.directUp(); - break; - default: - return; + for (int i = 0; i < fields.size(); i++) { + JCVariableDecl arg = fields.get(i); + JCModifiers mods = maker.Modifiers(GENERATED_MEMBER | Flags.PARAMETER, arg.mods.annotations); + params.append(maker.VarDef(mods, arg.name, arg.vartype, null)); } - if (paramNode.getKind() != Kind.ARGUMENT) return; - try { - declaration = (JCMethodDecl) paramNode.up().get(); - } catch (Exception e) { - return; + JCModifiers mods = maker.Modifiers(toJavacModifier(AccessLevel.PUBLIC) | COMPACT_RECORD_CONSTRUCTOR, List.<JCAnnotation>nil()); + JCBlock body = maker.Block(0L, List.<JCStatement>nil()); + JCMethodDecl constr = maker.MethodDef(mods, typeNode.toName("<init>"), null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), body, null); + return recursiveSetGeneratedBy(constr, source); + } + + /** + * If the provided typeNode is a record, returns the compact constructor (there should only be one, but if the file is + * not semantically sound there might be more). If the only one in existence is the default auto-generated one, it is removed, + * a new explicit one is created, and that one is returned in a list. + * + * Otherwise, an empty list is returned. + */ + private List<JCMethodDecl> addCompactConstructorIfNeeded(JavacNode typeNode, JavacNode source) { + List<JCMethodDecl> answer = List.nil(); + + if (typeNode == null || !(typeNode.get() instanceof JCClassDecl)) return answer; + + JCClassDecl cDecl = (JCClassDecl) typeNode.get(); + if ((cDecl.mods.flags & RECORD) == 0) return answer; + + ListBuffer<JCTree> newDefs = new ListBuffer<JCTree>(); + boolean generateConstructor = false; + + for (JCTree def : cDecl.defs) { + boolean remove = false; + if (def instanceof JCMethodDecl) { + JCMethodDecl md = (JCMethodDecl) def; + if (md.name.contentEquals("<init>")) { + if ((md.mods.flags & Flags.GENERATEDCONSTR) != 0) { + remove = true; + generateConstructor = true; + } else { + if (!isTolerate(typeNode, md)) { + if ((md.mods.flags & COMPACT_RECORD_CONSTRUCTOR) != 0) { + generateConstructor = false; + answer = answer.prepend(md); + } + } + } + } + } + if (!remove) newDefs.append(def); } - if (declaration.body == null) { - // This used to be a warning, but as @NonNull also has a documentary purpose, better to not warn about this. Since 1.16.7 - return; + if (generateConstructor) { + cDecl.defs = newDefs.toList(); + JCMethodDecl ctr = createRecordArgslessConstructor(typeNode, source); + injectMethod(typeNode, ctr); + answer = answer.prepend(ctr); } + return answer; + } + + private void addNullCheckIfNeeded(JCMethodDecl method, JavacNode paramNode, JavacNode source) { // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter, // 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), annotationNode); + JCStatement nullCheck = recursiveSetGeneratedBy(generateNullCheck(source.getTreeMaker(), paramNode, source), source); if (nullCheck == null) { // @NonNull applied to a primitive. Kinda pointless. Let's generate a warning. - annotationNode.addWarning("@NonNull is meaningless on a primitive."); + source.addWarning("@NonNull is meaningless on a primitive."); return; } - List<JCStatement> statements = declaration.body.stats; + List<JCStatement> statements = method.body.stats; String expectedName = paramNode.getName(); @@ -158,8 +199,71 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> { List<JCStatement> newList = tail.prepend(nullCheck); for (JCStatement stat : head) newList = newList.prepend(stat); - declaration.body.stats = newList; - annotationNode.getAst().setChanged(); + method.body.stats = newList; + source.getAst().setChanged(); + } + + @Override public void handle(AnnotationValues<NonNull> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.NON_NULL_FLAG_USAGE, "@NonNull"); + if (annotationNode.up().getKind() == Kind.FIELD) { + // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc), + // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to + // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning + // behaviour on _OUR_ 'lombok.NonNull'. + + try { + if (isPrimitive(((JCVariableDecl) annotationNode.up().get()).vartype)) { + annotationNode.addWarning("@NonNull is meaningless on a primitive."); + } + } catch (Exception ignore) {} + + JCVariableDecl fDecl = (JCVariableDecl) annotationNode.up().get(); + if ((fDecl.mods.flags & RECORD) != 0) { + // well, these kinda double as parameters (of the compact constructor), so we do some work here. + + List<JCMethodDecl> compactConstructors = addCompactConstructorIfNeeded(annotationNode.up().up(), annotationNode); + for (JCMethodDecl ctr : compactConstructors) { + addNullCheckIfNeeded(ctr, annotationNode.up(), annotationNode); + } + } + return; + } + + JCMethodDecl declaration; + JavacNode paramNode; + + switch (annotationNode.up().getKind()) { + case ARGUMENT: + paramNode = annotationNode.up(); + break; + case TYPE_USE: + JavacNode typeNode = annotationNode.directUp(); + paramNode = typeNode.directUp(); + break; + default: + return; + } + + if (paramNode.getKind() != Kind.ARGUMENT) return; + try { + declaration = (JCMethodDecl) paramNode.up().get(); + } catch (Exception e) { + return; + } + + if (declaration.body == null) { + // This used to be a warning, but as @NonNull also has a documentary purpose, better to not warn about this. Since 1.16.7 + return; + } + + if ((declaration.mods.flags & (GENERATED_MEMBER | COMPACT_RECORD_CONSTRUCTOR)) != 0) { + // The 'real' annotations are on the `record Foo(@NonNull Obj x)` part and we just see these + // syntax-sugared over. We deal with it on the field declaration variant, as those are always there, + // not dependent on whether you write out the compact constructor or not. + return; + } + + addNullCheckIfNeeded(declaration, paramNode, annotationNode); } public boolean isNullCheck(JCStatement stat) { 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 9943bcc8..a0634494 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.tree.JCTree.JCAnnotation; @@ -58,8 +57,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)) { @@ -68,13 +69,8 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { } } - JCClassDecl typeDecl = null; - if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); - long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; - boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0; - - if (typeDecl == null || notAClass) { - errorNode.addError("@Setter is only supported on a class or a field."); + if (!isClass(typeNode)) { + errorNode.addError(SETTER_NODE_NOT_SUPPORTED_ERR); return; } @@ -148,7 +144,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; } diff --git a/src/core/lombok/javac/handlers/HandleSneakyThrows.java b/src/core/lombok/javac/handlers/HandleSneakyThrows.java index 83f97c20..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,8 +35,7 @@ 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.JCAnnotation; @@ -53,7 +52,7 @@ 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) { diff --git a/src/core/lombok/javac/handlers/HandleStandardException.java b/src/core/lombok/javac/handlers/HandleStandardException.java new file mode 100644 index 00000000..ea1a0c5c --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleStandardException.java @@ -0,0 +1,218 @@ +/* + * 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.javac.handlers; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Name; +import lombok.AccessLevel; +import lombok.ConfigurationKeys; +import lombok.experimental.StandardException; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.delombok.LombokOptionsFactory; +import lombok.javac.Javac; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; +import lombok.javac.handlers.JavacHandlerUtil.*; +import lombok.spi.Provides; + +import static lombok.core.handlers.HandlerUtil.handleFlagUsage; +import static lombok.javac.Javac.*; +import static lombok.javac.handlers.JavacHandlerUtil.*; + +@Provides +public class HandleStandardException extends JavacAnnotationHandler<StandardException> { + @Override + public void handle(AnnotationValues<StandardException> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.STANDARD_EXCEPTION_FLAG_USAGE, "@StandardException"); + deleteAnnotationIfNeccessary(annotationNode, StandardException.class); + deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); + JavacNode typeNode = annotationNode.up(); + + if (!isClass(typeNode)) { + annotationNode.addError("@StandardException is only supported on a class."); + return; + } + + JCClassDecl classDef = (JCClassDecl) typeNode.get(); + if (classDef.extending == null) { + annotationNode.addError("@StandardException requires that you extend a Throwable type"); + return; + } + + AccessLevel access = annotation.getInstance().access(); + if (access == null) access = AccessLevel.PUBLIC; + if (access == AccessLevel.NONE) { + annotationNode.addError("AccessLevel.NONE is not valid here"); + access = AccessLevel.PUBLIC; + } + + generateNoArgsConstructor(typeNode, access, annotationNode); + generateMsgOnlyConstructor(typeNode, access, annotationNode); + generateCauseOnlyConstructor(typeNode, access, annotationNode); + generateFullConstructor(typeNode, access, annotationNode); + } + + private void generateNoArgsConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) { + if (hasConstructor(typeNode) != MemberExistsResult.NOT_EXISTS) return; + JavacTreeMaker maker = typeNode.getTreeMaker(); + + List<JCExpression> args = List.<JCExpression>of(maker.Literal(CTC_BOT, null), maker.Literal(CTC_BOT, null)); + JCStatement thisCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("this")), args)); + JCMethodDecl constr = createConstructor(level, typeNode, false, false, source, List.of(thisCall)); + injectMethod(typeNode, constr, List.<Type>nil(), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); + } + + private void generateMsgOnlyConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) { + if (hasConstructor(typeNode, String.class) != MemberExistsResult.NOT_EXISTS) return; + JavacTreeMaker maker = typeNode.getTreeMaker(); + + List<JCExpression> args = List.<JCExpression>of(maker.Ident(typeNode.toName("message")), maker.Literal(CTC_BOT, null)); + JCStatement thisCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("this")), args)); + JCMethodDecl constr = createConstructor(level, typeNode, true, false, source, List.of(thisCall)); + injectMethod(typeNode, constr, List.<Type>of(typeNode.getSymbolTable().stringType), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); + } + + private void generateCauseOnlyConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) { + if (hasConstructor(typeNode, Throwable.class) != MemberExistsResult.NOT_EXISTS) return; + JavacTreeMaker maker = typeNode.getTreeMaker(); + Name causeName = typeNode.toName("cause"); + + JCExpression causeDotGetMessage = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(causeName), typeNode.toName("getMessage")), List.<JCExpression>nil()); + JCExpression msgExpression = maker.Conditional(maker.Binary(CTC_NOT_EQUAL, maker.Ident(causeName), maker.Literal(CTC_BOT, null)), causeDotGetMessage, maker.Literal(CTC_BOT, null)); + + List<JCExpression> args = List.<JCExpression>of(msgExpression, maker.Ident(causeName)); + JCStatement thisCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("this")), args)); + JCMethodDecl constr = createConstructor(level, typeNode, false, true, source, List.of(thisCall)); + injectMethod(typeNode, constr, List.<Type>of(typeNode.getSymbolTable().throwableType), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); + } + + private void generateFullConstructor(JavacNode typeNode, AccessLevel level, JavacNode source) { + if (hasConstructor(typeNode, String.class, Throwable.class) != MemberExistsResult.NOT_EXISTS) return; + JavacTreeMaker maker = typeNode.getTreeMaker(); + + Name causeName = typeNode.toName("cause"); + Name superName = typeNode.toName("super"); + + List<JCExpression> args = List.<JCExpression>of(maker.Ident(typeNode.toName("message"))); + JCStatement superCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(superName), args)); + JCExpression causeNotNull = maker.Binary(CTC_NOT_EQUAL, maker.Ident(causeName), maker.Literal(CTC_BOT, null)); + JCStatement initCauseCall = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(superName), typeNode.toName("initCause")), List.<JCExpression>of(maker.Ident(causeName)))); + JCStatement initCause = maker.If(causeNotNull, initCauseCall, null); + JCMethodDecl constr = createConstructor(level, typeNode, true, true, source, List.of(superCall, initCause)); + injectMethod(typeNode, constr, List.<Type>of(typeNode.getSymbolTable().stringType, typeNode.getSymbolTable().throwableType), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); + } + + private static MemberExistsResult hasConstructor(JavacNode node, Class<?>... paramTypes) { + node = upToTypeNode(node); + + if (node != null && node.get() instanceof JCClassDecl) { + for (JCTree def : ((JCClassDecl) node.get()).defs) { + if (def instanceof JCMethodDecl) { + JCMethodDecl md = (JCMethodDecl) def; + if (md.name.contentEquals("<init>") && (md.mods.flags & Flags.GENERATEDCONSTR) == 0) { + if (!paramsMatch(node, md.params, paramTypes)) continue; + return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK; + } + } + } + } + + return MemberExistsResult.NOT_EXISTS; + } + + private static boolean paramsMatch(JavacNode node, List<JCVariableDecl> a, Class<?>[] b) { + if (a == null) return b == null || b.length == 0; + if (b == null) return a.size() == 0; + if (a.size() != b.length) return false; + + for (int i = 0; i < a.size(); i++) { + JCVariableDecl param = a.get(i); + Class<?> c = b[i]; + if (!typeMatches(c, node, param.vartype)) return false; + } + + return true; + } + + private static void addConstructorProperties(JCModifiers mods, JavacNode node, boolean msgParam, boolean causeParam) { + if (!msgParam && !causeParam) return; + JavacTreeMaker maker = node.getTreeMaker(); + JCExpression constructorPropertiesType = chainDots(node, "java", "beans", "ConstructorProperties"); + ListBuffer<JCExpression> fieldNames = new ListBuffer<JCExpression>(); + if (msgParam) fieldNames.append(maker.Literal("message")); + if (causeParam) fieldNames.append(maker.Literal("cause")); + JCExpression fieldNamesArray = maker.NewArray(null, List.<JCExpression>nil(), fieldNames.toList()); + JCAnnotation annotation = maker.Annotation(constructorPropertiesType, List.of(fieldNamesArray)); + mods.annotations = mods.annotations.append(annotation); + } + + @SuppressWarnings("deprecation") private static JCMethodDecl createConstructor(AccessLevel level, JavacNode typeNode, boolean msgParam, boolean causeParam, JavacNode source, List<JCStatement> statements) { + JavacTreeMaker maker = typeNode.getTreeMaker(); + + boolean addConstructorProperties; + if ((!msgParam && !causeParam) || isLocalType(typeNode) || !LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) { + addConstructorProperties = false; + } else { + Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES); + addConstructorProperties = v != null ? v.booleanValue() : + Boolean.FALSE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + } + + ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); + + if (msgParam) { + Name fieldName = typeNode.toName("message"); + long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); + JCExpression pType = genJavaLangTypeRef(typeNode, "String"); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), fieldName, pType, null); + params.append(param); + } + + if (causeParam) { + Name fieldName = typeNode.toName("cause"); + long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); + JCExpression pType = genJavaLangTypeRef(typeNode, "Throwable"); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), fieldName, pType, null); + params.append(param); + } + + JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil()); + if (addConstructorProperties) addConstructorProperties(mods, typeNode, msgParam, causeParam); + return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"), + null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), + maker.Block(0L, statements), null), source); + } + + public static boolean isLocalType(JavacNode type) { + Kind kind = type.up().getKind(); + if (kind == Kind.COMPILATION_UNIT) return false; + if (kind == Kind.TYPE) return isLocalType(type.up()); + return true; + } +} diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index d9e55a56..87b18576 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 @@ -32,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; @@ -84,8 +82,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 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"; @@ -112,7 +111,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; - builderClassName = fixBuilderClassName(node, ""); + builderClassName = getBuilderClassNameTemplate(node, null); } void setBuilderToImpl() { @@ -160,15 +159,15 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { List<JCExpression> superclassTypeParams = List.nil(); boolean addCleaning = false; - if (!(parent.get() instanceof JCClassDecl)) { - annotationNode.addError("@SuperBuilder is only supported on types."); + if (!isClass(parent)) { + annotationNode.addError("@SuperBuilder is only supported on classes."); return; } - // Gather all fields of the class that should be set by the builder. job.parentType = parent; JCClassDecl td = (JCClassDecl) parent.get(); - ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>(); + + // Gather all fields of the class that should be set by the builder. ArrayList<JavacNode> nonFinalNonDefaultedFields = null; boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); @@ -211,7 +210,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { } addObtainVia(bfd, fieldNode); job.builderFields.add(bfd); - allFields.append(fieldNode); } job.typeParams = job.builderTypeParams = td.typarams; @@ -234,8 +232,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { 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.add(maker.Ident(parent.toName(classGenericName))); - typeParamsForBuilder.add(maker.Ident(parent.toName(builderGenericName))); + 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()) { @@ -255,10 +253,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { } if (extendsClause instanceof JCFieldAccess) { Name superclassName = ((JCFieldAccess) extendsClause).getIdentifier(); - String superclassBuilderClassName = superclassName.toString() + "Builder"; + String builderClassNameTemplate = BuilderJob.getBuilderClassNameTemplate(annotationNode, null); + String superclassBuilderClassName = job.replaceBuilderClassName(superclassName.toString(), builderClassNameTemplate); superclassBuilderClass = parent.getTreeMaker().Select((JCFieldAccess) extendsClause, parent.toName(superclassBuilderClassName)); } else if (extendsClause != null) { - String superclassBuilderClassName = extendsClause.toString() + "Builder"; + String builderClassNameTemplate = BuilderJob.getBuilderClassNameTemplate(annotationNode, null); + String superclassBuilderClassName = job.replaceBuilderClassName(extendsClause.toString(), builderClassNameTemplate); superclassBuilderClass = chainDots(parent, extendsClause.toString(), superclassBuilderClassName); } @@ -305,7 +305,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { if (sd == null) continue; JavacSingularizer singularizer = sd.getSingularizer(); if (singularizer == null) continue; - if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderType, sd)) { + if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderAbstractType, sd)) { bfd.singularData = null; } } @@ -452,19 +452,19 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // Keep any type params of the annotated class. ListBuffer<JCTypeParameter> allTypeParams = new ListBuffer<JCTypeParameter>(); - allTypeParams.addAll(copyTypeParams(job.sourceNode, job.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, job.parentType, job.typeParams); - allTypeParams.add(maker.TypeParameter(job.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 = job.toName(job.builderClassName); ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(job.typeParams, maker, job.sourceNode); - typeParamsForBuilder.add(maker.Ident(job.toName(classGenericName))); - typeParamsForBuilder.add(maker.Ident(job.toName(builderGenericName))); + 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.add(maker.TypeParameter(job.toName(builderGenericName), List.<JCExpression>of(typeApply))); + allTypeParams.append(maker.TypeParameter(job.toName(builderGenericName), List.<JCExpression>of(typeApply))); JCExpression extending = null; if (superclassBuilderClass != null) { @@ -472,8 +472,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // 1. Add the type parameters of the superclass. typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker, job.sourceNode); // 2. Add the builder type params <C, B>. - typeParamsForBuilder.add(maker.Ident(job.toName(classGenericName))); - typeParamsForBuilder.add(maker.Ident(job.toName(builderGenericName))); + typeParamsForBuilder.append(maker.Ident(job.toName(classGenericName))); + typeParamsForBuilder.append(maker.Ident(job.toName(builderGenericName))); extending = maker.TypeApply(superclassBuilderClass, typeParamsForBuilder.toList()); } @@ -491,9 +491,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // Extend the abstract builder. JCExpression extending = namePlusTypeParamsToTypeReference(maker, job.parentType, job.toName(job.builderAbstractClassName), false, List.<JCTypeParameter>nil()); - // Add any type params of the annotated class. - ListBuffer<JCTypeParameter> allTypeParams = new ListBuffer<JCTypeParameter>(); - allTypeParams.addAll(copyTypeParams(job.sourceNode, job.typeParams)); + // 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, job.parentType, job.typeParams); @@ -501,8 +499,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { JCExpression builderImplClassExpression = namePlusTypeParamsToTypeReference(maker, job.parentType, job.toName(job.builderImplClassName), false, job.typeParams); ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(job.typeParams, maker, job.sourceNode); - typeParamsForBuilder.add(annotatedClass); - typeParamsForBuilder.add(builderImplClassExpression); + typeParamsForBuilder.append(annotatedClass); + typeParamsForBuilder.append(builderImplClassExpression); extending = maker.TypeApply(extending, typeParamsForBuilder.toList()); JCClassDecl builder = maker.ClassDef(mods, job.toName(job.builderImplClassName), copyTypeParams(job.parentType, job.typeParams), extending, List.<JCExpression>nil(), List.<JCTree>nil()); @@ -556,7 +554,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { } } - List<JCAnnotation> annsOnMethod = job.checkerFramework.generateUnique() ? List.of(maker.Annotation(genTypeRef(job.parentType, 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. @@ -566,9 +564,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { 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); + 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); @@ -591,9 +589,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { private JCMethodDecl generateBuilderMethod(SuperBuilderJob job) { JavacTreeMaker maker = job.getTreeMaker(); - ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); - for (JCTypeParameter typeParam : job.typeParams) typeArgs.append(maker.Ident(typeParam.name)); - 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); @@ -603,14 +598,17 @@ 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, job.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, job.parentType, job.toName(job.builderAbstractClassName), false, List.<JCTypeParameter>nil()), typeParameterNames.toList()); - - List<JCAnnotation> annsOnMethod = job.checkerFramework.generateUnique() ? List.of(maker.Annotation(genTypeRef(job.parentType, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil())) : List.<JCAnnotation>nil(); + 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; @@ -627,9 +625,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { private JCMethodDecl generateToBuilderMethod(SuperBuilderJob job) { JavacTreeMaker maker = job.getTreeMaker(); - ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); - for (JCTypeParameter typeParam : job.typeParams) typeArgs.append(maker.Ident(typeParam.name)); - 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); @@ -640,14 +635,14 @@ 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, job.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); + 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 = job.checkerFramework.generateUnique() ? List.of(maker.Annotation(genTypeRef(job.parentType, 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(); 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; @@ -728,9 +723,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { 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); + 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); @@ -738,7 +733,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // Call the builder's setter methods to fill the values from the instance. for (BuilderFieldData bfd : job.builderFields) { - JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, job.parentType, maker, setterPrefix); + JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, job, setterPrefix); body.append(exec); } @@ -747,22 +742,23 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { 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()); } } @@ -773,13 +769,12 @@ 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; } @@ -1090,13 +1085,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { 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, source)); + 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 f7ac50e7..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; @@ -51,7 +50,7 @@ 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"; @@ -77,6 +76,12 @@ public class HandleSynchronized extends JavacAnnotationHandler<Synchronized> { return; } + JavacNode typeNode = upToTypeNode(annotationNode); + if (!isClassOrEnum(typeNode)) { + annotationNode.addError("@Synchronized is legal only on methods in classes and enums."); + return; + } + boolean[] isStatic = new boolean[] {(method.mods.flags & Flags.STATIC) != 0}; String lockName = annotation.getInstance().value(); boolean autoMake = false; @@ -87,8 +92,6 @@ public class HandleSynchronized extends JavacAnnotationHandler<Synchronized> { JavacTreeMaker maker = methodNode.getTreeMaker().at(ast.pos); - JavacNode typeNode = upToTypeNode(annotationNode); - MemberExistsResult exists = MemberExistsResult.NOT_EXISTS; if (typeNode != null && typeNode.get() instanceof JCClassDecl) { diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index 6bf38a84..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,8 +38,7 @@ 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.JCAnnotation; @@ -59,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"); @@ -106,13 +105,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { public void generateToString(JavacNode typeNode, JavacNode source, java.util.List<Included<JavacNode, ToString.Include>> members, boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) { - boolean notAClass = true; - if (typeNode.get() instanceof JCClassDecl) { - long flags = ((JCClassDecl) typeNode.get()).mods.flags; - notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0; - } - - if (notAClass) { + if (!isClassOrEnum(typeNode)) { source.addError("@ToString is only supported on a class or enum."); return; } diff --git a/src/core/lombok/javac/handlers/HandleUtilityClass.java b/src/core/lombok/javac/handlers/HandleUtilityClass.java index cd05c285..6fa630e2 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 @@ -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.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCBlock; @@ -47,12 +45,13 @@ import lombok.experimental.UtilityClass; 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"); @@ -65,13 +64,8 @@ public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> { } private static boolean checkLegality(JavacNode typeNode, JavacNode errorNode) { - JCClassDecl typeDecl = null; - if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); - long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; - boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0; - - if (typeDecl == null || notAClass) { - errorNode.addError("@UtilityClass is only supported on a class (can't be an interface, enum, or annotation)."); + if (!isClass(typeNode)) { + errorNode.addError("@UtilityClass is only supported on a class."); return false; } diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 54645354..0ed831ab 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 { @@ -59,7 +58,7 @@ public class HandleVal extends JavacASTAdapter { } @SuppressWarnings("deprecation") @Override - public void visitLocal(JavacNode localNode, JCVariableDecl local) { + public void endVisitLocal(JavacNode localNode, JCVariableDecl local) { JCTree typeTree = local.vartype; if (typeTree == null) return; String typeTreeToString = typeTree.toString(); diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java index 503792fa..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(); diff --git a/src/core/lombok/javac/handlers/HandleWith.java b/src/core/lombok/javac/handlers/HandleWith.java index 249df096..47f78b1e 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.tree.JCTree.JCAnnotation; @@ -60,7 +59,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) { diff --git a/src/core/lombok/javac/handlers/HandleWithBy.java b/src/core/lombok/javac/handlers/HandleWithBy.java index ed0ed863..4ba4337e 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; @@ -65,7 +64,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) { @@ -124,7 +123,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(); @@ -136,7 +134,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."); diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 8233f20b..fba13133 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 @@ -103,6 +103,7 @@ import lombok.delombok.LombokOptionsFactory; import lombok.experimental.Accessors; import lombok.experimental.Tolerate; import lombok.javac.Javac; +import lombok.javac.JavacAugments; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; import lombok.permit.Permit; @@ -537,21 +538,18 @@ public class JavacHandlerUtil { public static void deleteImportFromCompilationUnit(JavacNode node, String name) { if (inNetbeansEditor(node)) return; if (!node.shouldDeleteLombokAnnotations()) return; - ListBuffer<JCTree> newDefs = new ListBuffer<JCTree>(); JCCompilationUnit unit = (JCCompilationUnit) node.top().get(); for (JCTree def : unit.defs) { - boolean delete = false; - if (def instanceof JCImport) { - JCImport imp0rt = (JCImport)def; - delete = (!imp0rt.staticImport && imp0rt.qualid.toString().equals(name)); - } - if (!delete) newDefs.append(def); + if (!(def instanceof JCImport)) continue; + JCImport imp0rt = (JCImport) def; + if (imp0rt.staticImport) continue; + if (!imp0rt.qualid.toString().equals(name)) continue; + JavacAugments.JCImport_deletable.set(imp0rt, true); } - unit.defs = newDefs.toList(); } - + private static List<JCAnnotation> filterList(List<JCAnnotation> annotations, JCTree jcTree) { ListBuffer<JCAnnotation> newAnnotations = new ListBuffer<JCAnnotation>(); for (JCAnnotation ann : annotations) { @@ -807,7 +805,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>")) { @@ -1972,17 +1970,30 @@ public class JavacHandlerUtil { return out.toList(); } - static boolean isClass(JavacNode typeNode) { - return isClassAndDoesNotHaveFlags(typeNode, Flags.INTERFACE | Flags.ENUM | Flags.ANNOTATION); + /** + * 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); + /** + * 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, int flags) { + 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; diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 1af4f673..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 @@ -298,7 +298,7 @@ public class JavacSingularsRecipes { 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); @@ -357,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")); } @@ -501,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/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/JavacJavaUtilListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java index 56a162e3..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,13 +24,12 @@ 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.JCCase; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -39,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"); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index 22f569f4..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,8 +36,7 @@ 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.JCBlock; @@ -48,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"); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java index 5a0638d8..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,19 +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.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"); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java index cf8fea9f..3b6113df 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java @@ -89,7 +89,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { 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); diff --git a/src/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java index 6ab4d3e5..8d39f447 100755 --- a/src/delombok/lombok/delombok/Delombok.java +++ b/src/delombok/lombok/delombok/Delombok.java @@ -60,7 +60,9 @@ import javax.tools.JavaFileObject; import lombok.Lombok; import lombok.javac.CommentCatcher; import lombok.javac.Javac; +import lombok.javac.JavacAugments; import lombok.javac.LombokOptions; +import lombok.javac.apt.LombokProcessor; import lombok.permit.Permit; import com.sun.tools.javac.code.Symtab; @@ -68,8 +70,11 @@ import com.sun.tools.javac.comp.Todo; import com.sun.tools.javac.file.BaseFileManager; import com.sun.tools.javac.main.Arguments; import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.ListBuffer; import com.zwitserloot.cmdreader.CmdReader; import com.zwitserloot.cmdreader.Description; import com.zwitserloot.cmdreader.Excludes; @@ -167,6 +172,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(); @@ -790,6 +799,16 @@ public class Delombok { if (verbose) feedback.printf("File: %s [%s]\n", unit.sourcefile.getName(), "unchanged (skipped)"); continue; } + ListBuffer<JCTree> newDefs = new ListBuffer<JCTree>(); + for (JCTree def : unit.defs) { + if (def instanceof JCImport) { + Boolean b = JavacAugments.JCImport_deletable.get((JCImport) def); + if (b == null || !b.booleanValue()) newDefs.append(def); + } else { + newDefs.append(def); + } + } + unit.defs = newDefs.toList(); if (verbose) feedback.printf("File: %s [%s%s]\n", unit.sourcefile.getName(), result.isChanged() ? "delomboked" : "unchanged", force && !options.isChanged(unit) ? " (forced)" : ""); Writer rawWriter; if (presetWriter != null) rawWriter = createUnicodeEscapeWriter(presetWriter); diff --git a/src/delombok/lombok/delombok/DelombokApp.java b/src/delombok/lombok/delombok/DelombokApp.java index 8467bf77..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 { 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 9f47e5ec..bcf3f431 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; @@ -279,7 +278,6 @@ public class PrettyPrinter extends JCTree.Visitor { aligned = false; } - private void println() { try { out.write(LINE_SEP); @@ -516,10 +514,12 @@ 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 & RECORD) != 0; if (isAnnotationInterface) print("@interface "); else if (isInterface) print("interface "); else if (isEnum) print("enum "); + else if (isRecord) print("record "); else print("class "); print(tree.name); @@ -531,6 +531,9 @@ public class PrettyPrinter extends JCTree.Visitor { print(tree.typarams, ", "); print(">"); } + + if (isRecord) printRecordConstructor(tree.defs); + JCTree extendsClause = getExtendsClause(tree); if (extendsClause != null) { print(" extends "); @@ -551,6 +554,23 @@ public class PrettyPrinter extends JCTree.Visitor { currentTypeName = prevTypeName; } + private void printRecordConstructor(List<JCTree> members) { + boolean first = true; + print("("); + for (JCTree member : members) { + if (member instanceof JCVariableDecl) { + JCVariableDecl variableDecl = (JCVariableDecl) member; + if ((variableDecl.mods.flags & GENERATED_MEMBER) != 0) { + if (!first) print(", "); + first = false; + printAnnotations(variableDecl.mods.annotations, false); + printVarDef0(variableDecl); + } + } + } + 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. @@ -580,6 +600,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 & GENERATED_MEMBER) != 0) continue; if (prefType != null && prefType != JCVariableDecl.class) println(); if (isInterface) flagMod = -1L & ~(PUBLIC | STATIC | FINAL); print(member); @@ -792,21 +813,28 @@ public class PrettyPrinter extends JCTree.Visitor { print(tree.name); } - boolean first = true; - print("("); - - JCVariableDecl recvparam = readObject(tree, "recvparam", null); - if (recvparam != null) { - printVarDefInline(recvparam); - first = false; + boolean argsLessConstructor = false; + if (isConstructor && (tree.mods.flags & COMPACT_RECORD_CONSTRUCTOR) != 0) { + argsLessConstructor = true; } - for (JCVariableDecl param : tree.params) { - if (!first) print(", "); - first = false; - printVarDefInline(param); + boolean first = true; + if (!argsLessConstructor) { + print("("); + + JCVariableDecl recvparam = readObject(tree, "recvparam", null); + if (recvparam != null) { + printVarDefInline(recvparam); + first = false; + } + + for (JCVariableDecl param : tree.params) { + if (!first) print(", "); + first = false; + printVarDefInline(param); + } + print(")"); } - print(")"); if (tree.thrown.nonEmpty()) { print(" throws "); @@ -1387,9 +1415,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) { diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java index 488d6eee..0a11cd7a 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 @@ -208,28 +208,9 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { private static void patchDisableLombokForCodeCleanup(ScriptManager sm) { 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")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.IfStatement")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.WhileStatement")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFix$ThisQualifierVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.MethodInvocation")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFix$ThisQualifierVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.FieldAccess")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFix$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.MethodInvocation")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFix$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.TypeDeclaration")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFix$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.QualifiedName")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFix$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName")) - // since JDT 3.20 - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFixCore$ThisQualifierVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.MethodInvocation")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFixCore$ThisQualifierVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.FieldAccess")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFixCore$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.MethodInvocation")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFixCore$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.TypeDeclaration")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFixCore$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.QualifiedName")) - .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFixCore$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName")) - // if a generated node has children we can just ignore them as well; - .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode")) - .request(StackRequest.PARAM1) - .valueMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "returnFalse", "boolean", "java.lang.Object")) + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTNode", "accept", "void", "org.eclipse.jdt.core.dom.ASTVisitor")) + .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isRefactoringVisitorAndGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.core.dom.ASTVisitor")) + .request(StackRequest.THIS, StackRequest.PARAM1) .build()); } @@ -365,6 +346,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { 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")) + .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameTypeParameterProcessor$RenameTypeParameterVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName")) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode")) .valueMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "returnTrue", "boolean", "java.lang.Object")) .request(StackRequest.PARAM1) @@ -586,6 +568,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { /* Set generated flag for QualifiedNames */ 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")) + .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.TypeReference")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM4) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void", @@ -594,6 +577,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { 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")) + .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.TypeReference")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM4) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void", @@ -697,13 +681,13 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Delegate", "handleDelegateForType", "boolean", "java.lang.Object")) .build()); - sm.addScript(ScriptBuilder.setSymbolDuringMethodCall() + 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.addScript(ScriptBuilder.addField() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() .fieldName("$delegateMethods") .fieldType("Ljava/util/Map;") .setPublic() @@ -711,10 +695,10 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .targetClass("org.eclipse.jdt.internal.core.CompilationUnit") .build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + 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", "getChildren", "java.lang.Object[]", "java.lang.Object", "java.lang.Object")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Delegate", "addGeneratedDelegateMethods", "java.lang.Object[]", "java.lang.Object", "java.lang.Object")) .build()); } @@ -772,11 +756,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() @@ -784,18 +769,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()); } @@ -843,33 +830,33 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .request(StackRequest.RETURN_VALUE) .request(StackRequest.THIS) .request(StackRequest.PARAM1) - .wrapMethod(new Hook(PATCH_EXTENSIONMETHOD, "resolveType", OBJECT_SIG, OBJECT_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()); 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", PROBLEM_REPORTER_SIG, AST_NODE, METHOD_BINDING_SIG, MESSAGE_SEND_SIG)) + .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "nonStaticAccessToStaticMethod", "void", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) .requestExtra(StackRequest.THIS) .build()); diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java index 99367a22..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-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 @@ -148,7 +148,9 @@ public class PatchDelegate { CompilationUnitDeclaration cud = scope.compilationUnitScope().referenceContext; if (scope == scope.compilationUnitScope().topLevelTypes[0].scope) { - cleanupDelegateMethods(cud); + if (eclipseAvailable) { + EclipseOnlyMethods.cleanupDelegateMethods(cud); + } } if (!hasDelegateMarkedFieldsOrMethods(scope.referenceContext)) return false; @@ -195,8 +197,8 @@ public class PatchDelegate { } } finally { stack.remove(stack.size() - 1); - if (stack.isEmpty()) { - notifyDelegateMethodsAdded(cud); + if (stack.isEmpty() && eclipseAvailable) { + EclipseOnlyMethods.notifyDelegateMethodsAdded(cud); } } } @@ -204,16 +206,6 @@ public class PatchDelegate { return false; } - public static IJavaElement[] getChildren(IJavaElement[] returnValue, SourceTypeElementInfo javaElement) { - if (Symbols.hasSymbol("lombok.skipdelegates")) return returnValue; - - List<SourceMethod> delegateMethods = getDelegateMethods((SourceType) javaElement.getHandle()); - if (delegateMethods != null) { - return concat(returnValue, delegateMethods.toArray(new IJavaElement[0]), IJavaElement.class); - } - return returnValue; - } - /** * Returns a string containing the signature of a method that appears (erased) at least twice in the list. * If no duplicates are present, {@code null} is returned. @@ -459,11 +451,7 @@ public class PatchDelegate { private static void generateDelegateMethods(EclipseNode typeNode, List<BindingTuple> methods, DelegateReceiver delegateReceiver) { CompilationUnitDeclaration top = (CompilationUnitDeclaration) typeNode.top().get(); - - String qualifiedName = new String(CharOperation.concatWith(getQualifiedInnerName(typeNode.up(), typeNode.getName().toCharArray()), '$')); - SourceType sourceType = getSourceType(top, qualifiedName); - List<SourceMethod> delegateSourceMethods = getDelegateMethods(sourceType); - + 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); @@ -471,12 +459,12 @@ public class PatchDelegate { SetGeneratedByVisitor visitor = new SetGeneratedByVisitor(annNode.get()); method.traverse(visitor, ((TypeDeclaration)typeNode.get()).scope); injectMethod(typeNode, method); - - if (delegateSourceMethods != null) { - delegateSourceMethods.add(DelegateSourceMethod.forMethodDeclaration(sourceType, method)); - } + addedMethods.add(method); } } + if (eclipseAvailable) { + EclipseOnlyMethods.collectGeneratedDelegateMethods(top, typeNode, addedMethods); + } } public static void checkConflictOfTypeVarNames(BindingTuple binding, EclipseNode typeNode) throws CantMakeDelegates { @@ -722,127 +710,159 @@ public class PatchDelegate { return method; } - private static void cleanupDelegateMethods(CompilationUnitDeclaration cud) { - CompilationUnit compilationUnit = getCompilationUnit(cud); - if (compilationUnit != null) { - EclipseAugments.CompilationUnit_delegateMethods.clear(compilationUnit); + private static boolean eclipseAvailable = true; + static { + try { + CompilationUnit.class.getName(); + } catch (Throwable t) { + eclipseAvailable = false; } } - private static boolean javaModelManagerAvailable = true; - private static void notifyDelegateMethodsAdded(CompilationUnitDeclaration cud) { - CompilationUnit compilationUnit = getCompilationUnit(cud); - if (compilationUnit != null && javaModelManagerAvailable) { - try { - DeltaProcessor deltaProcessor = JavaModelManager.getJavaModelManager().getDeltaProcessor(); - deltaProcessor.fire(new JavaElementDelta(compilationUnit), ElementChangedEvent.POST_CHANGE); - } catch (NoClassDefFoundError e) { - javaModelManagerAvailable = false; - } - } + public static Object[] addGeneratedDelegateMethods(Object[] returnValue, Object javaElement) { + if (Symbols.hasSymbol("lombok.skipdelegates")) return returnValue; + if (!eclipseAvailable) return returnValue; + + return EclipseOnlyMethods.addGeneratedDelegateMethodsToChildren(returnValue, javaElement); } - private static CompilationUnit getCompilationUnit(Object iCompilationUnit) { - if (iCompilationUnit instanceof CompilationUnit) { - CompilationUnit compilationUnit = (CompilationUnit) iCompilationUnit; - return compilationUnit.originalFromClone(); + public static class EclipseOnlyMethods { + private static void cleanupDelegateMethods(CompilationUnitDeclaration cud) { + CompilationUnit compilationUnit = getCompilationUnit(cud); + if (compilationUnit != null) { + EclipseAugments.CompilationUnit_delegateMethods.clear(compilationUnit); + } } - 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); + 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)); } - 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); + 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; } - @Override public Object getElementInfo() throws JavaModelException { - return sourceMethodInfo; + 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); + } } - /** - * Disable refactoring for delegate methods - */ - @Override public boolean isReadOnly() { - return true; + private static CompilationUnit getCompilationUnit(Object iCompilationUnit) { + if (iCompilationUnit instanceof CompilationUnit) { + CompilationUnit compilationUnit = (CompilationUnit) iCompilationUnit; + return compilationUnit.originalFromClone(); + } + return null; } - /** - * This is required to prevent duplicate entries in the outline - */ - @Override public boolean equals(Object o) { - return this == o; + private static CompilationUnit getCompilationUnit(CompilationUnitDeclaration cud) { + return getCompilationUnit(cud.compilationResult.compilationUnit); } - 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); + 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); } - setArgumentNames(argumentNames); - - setSourceRangeStart(pS); - setSourceRangeEnd(pE); - setNameSourceStart(pS); - setNameSourceEnd(pE); - - setExceptionTypeNames(CharOperation.NO_CHAR_CHAR); - setReturnType(md.returnType == null ? new char[]{'v', 'o','i', 'd'} : CharOperation.concatWith(md.returnType.getParameterizedTypeName(), '.')); - setFlags(md.modifiers); } } - } - - private static List<SourceMethod> getDelegateMethods(SourceType sourceType) { - if (sourceType != null) { - CompilationUnit compilationUnit = getCompilationUnit(sourceType.getCompilationUnit()); - if (compilationUnit != null) { - ConcurrentMap<String, List<SourceMethod>> map = 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; + + 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; } - 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; + + 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 } - } catch (JavaModelException e) { - // Ignore } + return null; } - return null; } private static final class Reflection { diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java index 3d8c86c9..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-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 @@ -31,34 +31,44 @@ public class PatchDelegatePortal { static final String SOURCE_TYPE_ELEMENT_INFO = "org.eclipse.jdt.internal.core.SourceTypeElementInfo"; public static boolean handleDelegateForType(Object classScope) { - Boolean v = (Boolean) Permit.invokeSneaky(Reflection.problem, Reflection.handleDelegateForType, null, classScope); + Boolean v = (Boolean) Permit.invokeSneaky(Reflection.problemHandleDelegate, Reflection.handleDelegateForType, null, classScope); if (v == null) return false; return v.booleanValue(); } - public static Object[] getChildren(Object returnValue, Object javaElement) { - return (Object[]) Permit.invokeSneaky(Reflection.problem, Reflection.getChildren, null, returnValue, javaElement); + 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 Method getChildren; - public static final Throwable problem; + public static final Method addGeneratedDelegateMethods; + public static final Throwable problemHandleDelegate; + public static final Throwable problemAddGeneratedDelegateMethods; static { Method m = null, n = null; - Throwable problem_ = null; + Throwable problemHandleDelegate_ = null; + Throwable problemAddGeneratedDelegateMethods_ = null; try { m = Permit.getMethod(PatchDelegate.class, "handleDelegateForType", Class.forName(CLASS_SCOPE)); - n = Permit.getMethod(PatchDelegate.class, "getChildren", Class.forName(I_JAVA_ELEMENT_ARRAY), Class.forName(SOURCE_TYPE_ELEMENT_INFO)); } catch (Throwable t) { // That's problematic, but as long as no local classes are used we don't actually need it. // Better fail on local classes than crash altogether. - problem_ = t; + problemHandleDelegate_ = t; } handleDelegateForType = m; - getChildren = n; - 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 5f229955..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-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 @@ -55,6 +55,7 @@ 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.MethodBinding; @@ -143,14 +144,17 @@ public class PatchExtensionMethod { 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); } } @@ -246,7 +250,7 @@ public class PatchExtensionMethod { MessageSend_postponedErrors.set(messageSend, new PostponedNonStaticAccessToStaticMethodError(problemReporter, location, method)); } - public static Object resolveType(Object resolvedType, MessageSend methodCall, BlockScope scope) { + public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) { List<Extension> extensions = new ArrayList<Extension>(); TypeDeclaration decl = scope.classScope().referenceContext; diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java deleted file mode 100644 index 82df71f6..00000000 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java +++ /dev/null @@ -1,115 +0,0 @@ -package lombok.eclipse.agent; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import lombok.Lombok; - -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 Object resolveType(Object resolvedType, Object methodCall, Object scope) { - try { - return 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 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 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) { - handleReflectionDebug(e); - throw Lombok.sneakyThrow(e); - } catch (InvocationTargetException e) { - handleReflectionDebug(e.getCause()); - throw Lombok.sneakyThrow(e.getCause()); - } catch (NullPointerException e) { - handleReflectionDebug(e); - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - } - } - - public static boolean isDebugReflection() { - return !"false".equals(System.getProperty("lombok.debug.reflection", "false")); - } - - public static void handleReflectionDebug(Throwable t) { - if (!isDebugReflection()) return; - - System.err.println("** LOMBOK REFLECTION exception: " + t.getClass() + ": " + (t.getMessage() == null ? "(no message)" : t.getMessage())); - t.printStackTrace(System.err); - if (Reflection.problem != null) { - System.err.println("*** ADDITIONALLY, exception occurred setting up reflection: "); - Reflection.problem.printStackTrace(System.err); - } - } - - 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", Object.class, 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/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index f22e78a8..774e5b40 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; @@ -145,7 +120,7 @@ public class PatchVal { public static boolean couldBe(ImportReference[] imports, String key, TypeReference ref) { String[] keyParts = key.split("\\."); if (ref instanceof SingleTypeReference) { - char[] token = ((SingleTypeReference)ref).token; + char[] token = ((SingleTypeReference) ref).token; if (!matches(keyParts[keyParts.length - 1], token)) return false; if (imports == null) return true; top: @@ -165,7 +140,7 @@ public class PatchVal { } if (ref instanceof QualifiedTypeReference) { - char[][] tokens = ((QualifiedTypeReference)ref).tokens; + char[][] tokens = ((QualifiedTypeReference) ref).tokens; if (keyParts.length != tokens.length) return false; for(int i = 0; i < tokens.length; ++i) { String part = keyParts[i]; @@ -295,7 +270,6 @@ public class PatchVal { if (val) local.modifiers |= ClassFileConstants.AccFinal; local.annotations = addValAnnotation(local.annotations, local.type, scope); local.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(local.type, 3)); - return false; } diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java index e2d266c5..bee30922 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 @@ -43,20 +43,14 @@ import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.search.SearchMatch; -import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; 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.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; @@ -116,6 +110,15 @@ 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)); @@ -141,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. */ @@ -193,7 +204,6 @@ final class PatchFixesHider { } public static void transform(Object parser, Object ast) throws IOException { - Main.prependClassLoader(parser.getClass().getClassLoader()); init(parser.getClass().getClassLoader()); Util.invokeMethod(TRANSFORM, parser, ast); } @@ -207,20 +217,20 @@ final class PatchFixesHider { /** Contains patch code to support {@code @Delegate} */ public static final class Delegate { private static final Method HANDLE_DELEGATE_FOR_TYPE; - private static final Method GET_CHILDREN; + 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); - GET_CHILDREN = Util.findMethod(shadowed, "getChildren", Object.class, 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[] getChildren(Object returnValue, Object javaElement) { - return (Object[]) Util.invokeMethod(GET_CHILDREN, returnValue, javaElement); + public static Object[] addGeneratedDelegateMethods(Object returnValue, Object javaElement) { + return (Object[]) Util.invokeMethod(ADD_GENERATED_DELEGATE_METHODS, returnValue, javaElement); } } @@ -258,38 +268,67 @@ 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; @@ -297,30 +336,30 @@ final class PatchFixesHider { static { Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchExtensionMethod"); - RESOLVE_TYPE = Util.findMethod(shadowed, "resolveType", Object.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); - NON_STATIC_ACCESS_TO_STATIC_METHOD = Util.findMethod(shadowed, "nonStaticAccessToStaticMethod", ProblemReporter.class, ASTNode.class, MethodBinding.class, MessageSend.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 Object resolveType(Object resolvedType, MessageSend methodCall, BlockScope 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(ProblemReporter problemReporter, ASTNode location, MethodBinding method, MessageSend messageSend) { + public static void nonStaticAccessToStaticMethod(Object problemReporter, Object location, Object method, Object messageSend) { Util.invokeMethod(NON_STATIC_ACCESS_TO_STATIC_METHOD, problemReporter, location, method, messageSend); } } @@ -374,7 +413,25 @@ final class PatchFixesHider { } return result; } - + + public static boolean isRefactoringVisitorAndGenerated(org.eclipse.jdt.core.dom.ASTNode node, org.eclipse.jdt.core.dom.ASTVisitor visitor) { + if (visitor == null) return false; + + String className = visitor.getClass().getName(); + if (!(className.startsWith("org.eclipse.jdt.internal.corext.fix") || className.startsWith("org.eclipse.jdt.internal.ui.fix"))) return false; + + boolean result = false; + try { + result = ((Boolean)node.getClass().getField("$isGenerated").get(node)).booleanValue(); + if (!result && node.getParent() != null && node.getParent() instanceof org.eclipse.jdt.core.dom.QualifiedName) { + result = isGenerated(node.getParent()); + } + } catch (Exception e) { + // better to assume it isn't generated + } + return result; + } + public static boolean isListRewriteOnGeneratedNode(org.eclipse.jdt.core.dom.rewrite.ListRewrite rewrite) { return isGenerated(rewrite.getParent()); } 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 ff539704..af65bdf8 100644 --- a/src/launch/lombok/launch/Main.java +++ b/src/launch/lombok/launch/Main.java @@ -36,7 +36,7 @@ class Main { static synchronized void prependClassLoader(ClassLoader loader) { getShadowClassLoader(); - classLoader.prepend(loader); + classLoader.prependParent(loader); } public static void main(String[] args) throws Throwable { diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java index e75c300e..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,14 +103,12 @@ class ShadowClassLoader extends ClassLoader { private final List<String> parentExclusion = new ArrayList<String>(); private final List<String> highlanders = new ArrayList<String>(); - private final List<ClassLoader> prependedLoaders = new ArrayList<ClassLoader>(); + private final Set<ClassLoader> prependedParentLoaders = Collections.newSetFromMap(new IdentityHashMap<ClassLoader, Boolean>()); - public void prepend(ClassLoader loader) { + public void prependParent(ClassLoader loader) { if (loader == null) return; - for (ClassLoader cl : prependedLoaders) { - if (cl == loader) return; - } - prependedLoaders.add(loader); + if (loader == getParent()) return; + prependedParentLoaders.add(loader); } /** @@ -539,23 +538,25 @@ class ShadowClassLoader extends ClassLoader { } String fileNameOfClass = name.replace(".", "/") + ".class"; - for (ClassLoader pre : prependedLoaders) { - try { - URL res = pre.getResource(fileNameOfClass); - if (res == null) continue; - return urlToDefineClass(name, res, resolve); - } catch (Exception e) { - continue; - } - } - 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); 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/Symtab.java b/src/stubs/com/sun/tools/javac/code/Symtab.java index 8d823531..89ed5478 100644 --- a/src/stubs/com/sun/tools/javac/code/Symtab.java +++ b/src/stubs/com/sun/tools/javac/code/Symtab.java @@ -16,7 +16,9 @@ public class Symtab { public static Symtab instance(Context context) {return null;} public Type unknownType; public TypeSymbol noSymbol; - + public Type stringType; + public Type throwableType; + // JDK 9 public ModuleSymbol unnamedModule; } 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/website/WebsiteMaker.java b/src/support/lombok/website/WebsiteMaker.java index d786e605..ea472a19 100644 --- a/src/support/lombok/website/WebsiteMaker.java +++ b/src/support/lombok/website/WebsiteMaker.java @@ -1,3 +1,24 @@ +/* + * 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.website; import java.io.BufferedReader; @@ -72,7 +93,7 @@ public class WebsiteMaker { private static void printAllVersions(Domain domain) throws Exception { List<List<String>> versions = readAllVersions(domain); for (List<String> v : versions) { - System.out.println(" <a href=\"" + v.get(0) + "\">" + v.get(1) + "</a>"); + System.out.println(" <a href=\"" + v.get(1) + "\">" + v.get(0) + "</a>"); } } diff --git a/src/utils/lombok/eclipse/Eclipse.java b/src/utils/lombok/eclipse/Eclipse.java index 31979955..8af481b9 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,13 @@ public class Eclipse { */ public static final int ECLIPSE_DO_NOT_TOUCH_FLAG = ASTNode.Bit24; + /* This section includes flags that are in ecj files too new vs. the deps we compile against. + * Specifically: org.eclipse.jdt.internal.compiler.ast.ASTNode and org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers + */ + public static final int AccRecord = ASTNode.Bit25; // ECM.AccRecord + public static final int IsCanonicalConstructor = ASTNode.Bit10; // ASTNode.IsCanonicalConstructor + public static final int IsImplicit = ASTNode.Bit11; // ASTNode.IsImplicit + private static final Pattern SPLIT_AT_DOT = Pattern.compile("\\."); private Eclipse() { @@ -239,9 +246,14 @@ public class Eclipse { for (Field f : CompilerOptions.class.getDeclaredFields()) { try { - if (f.getName().startsWith("VERSION_1_")) { - ecjCompilerVersionCached = Math.max(ecjCompilerVersionCached, Integer.parseInt(f.getName().substring("VERSION_1_".length()))); - } + 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/eclipse/Java14Bits.java b/src/utils/lombok/eclipse/Java14Bits.java new file mode 100644 index 00000000..c7e00c39 --- /dev/null +++ b/src/utils/lombok/eclipse/Java14Bits.java @@ -0,0 +1,11 @@ +package lombok.eclipse; + +import org.eclipse.jdt.internal.compiler.ast.ASTNode; + +public class Java14Bits { + private Java14Bits() { } + + public static final int AccRecord = ASTNode.Bit25; + public static final int IsCanonicalConstructor = ASTNode.Bit10; // record declaration + public static final int IsImplicit = ASTNode.Bit11; // record declaration / generated statements in compact constructor +} diff --git a/src/utils/lombok/javac/Java14Flags.java b/src/utils/lombok/javac/Java14Flags.java new file mode 100644 index 00000000..0d565dca --- /dev/null +++ b/src/utils/lombok/javac/Java14Flags.java @@ -0,0 +1,26 @@ +package lombok.javac; + +public class Java14Flags { + private Java14Flags() { } + + /** + * Flag to indicate that a class is a record. The flag is also used to mark fields that are + * part of the state vector of a record and to mark the canonical constructor + */ + public static final long RECORD = 1L<<61; // ClassSymbols, MethodSymbols and VarSymbols + + /** + * Flag to mark a record constructor as a compact one + */ + public static final long COMPACT_RECORD_CONSTRUCTOR = 1L<<51; // MethodSymbols only + + /** + * Flag to mark a record field that was not initialized in the compact constructor + */ + public static final long UNINITIALIZED_FIELD= 1L<<51; // VarSymbols only + + /** Flag is set for compiler-generated record members, it could be appplied to + * accessors and fields + */ + public static final int GENERATED_MEMBER = 1<<24; // MethodSymbols and VarSymbols +} diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java index 33bd7265..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,6 +285,17 @@ 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; @@ -302,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); @@ -315,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/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/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; |