diff options
Diffstat (limited to 'src/core/lombok/javac')
26 files changed, 986 insertions, 354 deletions
diff --git a/src/core/lombok/javac/CapturingDiagnosticListener.java b/src/core/lombok/javac/CapturingDiagnosticListener.java index a0ac6adc..0e64ed8d 100644 --- a/src/core/lombok/javac/CapturingDiagnosticListener.java +++ b/src/core/lombok/javac/CapturingDiagnosticListener.java @@ -52,6 +52,10 @@ public class CapturingDiagnosticListener implements DiagnosticListener<JavaFileO "^" + Pattern.quote(file.getAbsolutePath()) + "\\s*:\\s*\\d+\\s*:\\s*(?:warning:\\s*)?(.*)$", Pattern.DOTALL).matcher(msg); if (m.matches()) msg = m.group(1); + if (msg.equals("deprecated item is not annotated with @Deprecated")) { + // This is new in JDK9; prior to that you don't see this. We shall ignore these. + return; + } messages.add(new CompilerMessage(d.getLineNumber(), d.getStartPosition(), d.getKind() == Kind.ERROR, msg)); } diff --git a/src/core/lombok/javac/CompilerMessageSuppressor.java b/src/core/lombok/javac/CompilerMessageSuppressor.java index a17e0c62..391ec64a 100644 --- a/src/core/lombok/javac/CompilerMessageSuppressor.java +++ b/src/core/lombok/javac/CompilerMessageSuppressor.java @@ -26,6 +26,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.util.LinkedList; +import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -43,38 +44,40 @@ import com.sun.tools.javac.util.Log; * then they will be generated AGAIN, this time with proper names and line numbers, at the end. Therefore, we want to suppress the logger. */ public final class CompilerMessageSuppressor { + private final Log log; - private static final Field errWriterField, warnWriterField, noticeWriterField, dumpOnErrorField, promptOnErrorField, diagnosticListenerField; + private static final WriterField errWriterField, warnWriterField, noticeWriterField; + private static final Field dumpOnErrorField, promptOnErrorField, diagnosticListenerField; private static final Field deferDiagnosticsField, deferredDiagnosticsField, diagnosticHandlerField; private static final ConcurrentMap<Class<?>, Field> handlerDeferredFields = new ConcurrentHashMap<Class<?>, Field>(); private static final Field NULL_FIELD; - private PrintWriter errWriter, warnWriter, noticeWriter; private Boolean dumpOnError, promptOnError; private DiagnosticListener<?> contextDiagnosticListener, logDiagnosticListener; private final Context context; - // If this is true, the fields changed. Better to print weird error messages than to fail outright. - private static final boolean dontBother; - private static final ThreadLocal<Queue<?>> queueCache = new ThreadLocal<Queue<?>>(); + enum Writers { + ERROR("errWriter", "ERROR"), + WARNING("warnWriter", "WARNING"), + NOTICE("noticeWriter", "NOTICE"); + + final String fieldName; + final String keyName; + + Writers(String fieldName, String keyName) { + this.fieldName = fieldName; + this.keyName = keyName; + } + } + static { - errWriterField = getDeclaredField(Log.class, "errWriter"); - warnWriterField = getDeclaredField(Log.class, "warnWriter"); - noticeWriterField = getDeclaredField(Log.class, "noticeWriter"); + errWriterField = createWriterField(Writers.ERROR); + warnWriterField = createWriterField(Writers.WARNING); + noticeWriterField = createWriterField(Writers.NOTICE); dumpOnErrorField = getDeclaredField(Log.class, "dumpOnError"); promptOnErrorField = getDeclaredField(Log.class, "promptOnError"); diagnosticListenerField = getDeclaredField(Log.class, "diagListener"); - - dontBother = - errWriterField == null || - warnWriterField == null || - noticeWriterField == null || - dumpOnErrorField == null || - promptOnErrorField == null || - diagnosticListenerField == null; - - deferDiagnosticsField = getDeclaredField(Log.class, "deferDiagnostics"); deferredDiagnosticsField = getDeclaredField(Log.class, "deferredDiagnostics"); @@ -100,17 +103,13 @@ public final class CompilerMessageSuppressor { this.context = context; } - public boolean disableLoggers() { + public void disableLoggers() { contextDiagnosticListener = context.get(DiagnosticListener.class); context.put(DiagnosticListener.class, (DiagnosticListener<?>) null); - if (dontBother) return false; - boolean dontBotherInstance = false; - - PrintWriter dummyWriter = new PrintWriter(new OutputStream() { - @Override public void write(int b) throws IOException { - // Do nothing on purpose - } - }); + + errWriterField.pauze(log); + warnWriterField.pauze(log); + noticeWriterField.pauze(log); if (deferDiagnosticsField != null) try { if (Boolean.TRUE.equals(deferDiagnosticsField.get(log))) { @@ -130,50 +129,23 @@ public final class CompilerMessageSuppressor { } } catch (Exception e) {} - if (!dontBotherInstance) try { - errWriter = (PrintWriter) errWriterField.get(log); - errWriterField.set(log, dummyWriter); - } catch (Exception e) { - dontBotherInstance = true; - } - - if (!dontBotherInstance) try { - warnWriter = (PrintWriter) warnWriterField.get(log); - warnWriterField.set(log, dummyWriter); - } catch (Exception e) { - dontBotherInstance = true; - } - - if (!dontBotherInstance) try { - noticeWriter = (PrintWriter) noticeWriterField.get(log); - noticeWriterField.set(log, dummyWriter); - } catch (Exception e) { - dontBotherInstance = true; - } - - if (!dontBotherInstance) try { + if (dumpOnErrorField != null) try { dumpOnError = (Boolean) dumpOnErrorField.get(log); dumpOnErrorField.set(log, false); } catch (Exception e) { - dontBotherInstance = true; } - if (!dontBotherInstance) try { + if (promptOnErrorField != null) try { promptOnError = (Boolean) promptOnErrorField.get(log); promptOnErrorField.set(log, false); } catch (Exception e) { - dontBotherInstance = true; } - if (!dontBotherInstance) try { + if (diagnosticListenerField != null) try { logDiagnosticListener = (DiagnosticListener<?>) diagnosticListenerField.get(log); diagnosticListenerField.set(log, null); } catch (Exception e) { - dontBotherInstance = true; } - - if (dontBotherInstance) enableLoggers(); - return !dontBotherInstance; } private static Field getDeferredField(Object handler) { @@ -193,20 +165,9 @@ public final class CompilerMessageSuppressor { contextDiagnosticListener = null; } - if (errWriter != null) try { - errWriterField.set(log, errWriter); - errWriter = null; - } catch (Exception e) {} - - if (warnWriter != null) try { - warnWriterField.set(log, warnWriter); - warnWriter = null; - } catch (Exception e) {} - - if (noticeWriter != null) try { - noticeWriterField.set(log, noticeWriter); - noticeWriter = null; - } catch (Exception e) {} + errWriterField.resume(log); + warnWriterField.resume(log); + noticeWriterField.resume(log); if (dumpOnError != null) try { dumpOnErrorField.set(log, dumpOnError); @@ -283,4 +244,107 @@ public final class CompilerMessageSuppressor { // javac will contain rather a lot of messages, but this is a lot better than just crashing during compilation! } } + + private static WriterField createWriterField(Writers w) { + // jdk9 + try { + Field writers = getDeclaredField(Log.class, "writer"); + if (writers != null) { + Class<?> kindsClass = Class.forName("com.sun.tools.javac.util.Log$WriterKind"); + for (Object enumConstant : kindsClass.getEnumConstants()) { + if (enumConstant.toString().equals(w.keyName)) { + return new Java9WriterField(writers, enumConstant); + } + } + return WriterField.NONE; + } + } catch (Exception e) { + } + + // jdk8 + Field writerField = getDeclaredField(Log.class, w.fieldName); + if (writerField != null) return new Java8WriterField(writerField); + + // other jdk + return WriterField.NONE; + } + + interface WriterField { + final PrintWriter NO_WRITER = new PrintWriter(new OutputStream() { + @Override public void write(int b) throws IOException { + // Do nothing on purpose + } + }); + + final WriterField NONE = new WriterField() { + @Override public void pauze(Log log) { + // do nothing + } + @Override public void resume(Log log) { + // no nothing + } + }; + + void pauze(Log log); + void resume(Log log); + } + + static class Java8WriterField implements WriterField { + private final Field field; + private PrintWriter writer; + + public Java8WriterField(Field field) { + this.field = field; + } + + @Override public void pauze(Log log) { + try { + writer = (PrintWriter) field.get(log); + field.set(log, NO_WRITER); + } catch (Exception e) { + } + } + + @Override public void resume(Log log) { + if (writer != null) { + try { + field.set(log, writer); + } catch (Exception e) { + } + } + writer = null; + } + } + + + static class Java9WriterField implements WriterField { + private final Field field; + private final Object key; + private PrintWriter writer; + + public Java9WriterField(Field field, Object key) { + this.field = field; + this.key = key; + } + + @Override public void pauze(Log log) { + try { + @SuppressWarnings("unchecked") Map<Object,PrintWriter> map = (Map<Object,PrintWriter>)field.get(log); + writer = map.get(key); + map.put(key, NO_WRITER); + } catch (Exception e) { + } + } + + @Override public void resume(Log log) { + if (writer != null) { + try { + @SuppressWarnings("unchecked") Map<Object,PrintWriter> map = (Map<Object,PrintWriter>)field.get(log); + map.put(key, writer); + } catch (Exception e) { + } + } + writer = null; + } + } }
\ No newline at end of file diff --git a/src/core/lombok/javac/Javac8BasedLombokOptions.java b/src/core/lombok/javac/Javac8BasedLombokOptions.java index 3fdea890..9a662490 100644 --- a/src/core/lombok/javac/Javac8BasedLombokOptions.java +++ b/src/core/lombok/javac/Javac8BasedLombokOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Project Lombok Authors. + * Copyright (C) 2013-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,7 +28,7 @@ import com.sun.tools.javac.util.Options; public class Javac8BasedLombokOptions extends LombokOptions { public static Javac8BasedLombokOptions replaceWithDelombokOptions(Context context) { Options options = Options.instance(context); - context.put(optionsKey, (Options)null); + context.put(optionsKey, (Options) null); Javac8BasedLombokOptions result = new Javac8BasedLombokOptions(context); result.putAll(options); return result; diff --git a/src/core/lombok/javac/Javac9BasedLombokOptions.java b/src/core/lombok/javac/Javac9BasedLombokOptions.java new file mode 100644 index 00000000..e786346d --- /dev/null +++ b/src/core/lombok/javac/Javac9BasedLombokOptions.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 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; + +import com.sun.tools.javac.main.Option; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Options; + +public class Javac9BasedLombokOptions extends LombokOptions { + public static Javac9BasedLombokOptions replaceWithDelombokOptions(Context context) { + Options options = Options.instance(context); + context.put(optionsKey, (Options) null); + Javac9BasedLombokOptions result = new Javac9BasedLombokOptions(context); + result.putAll(options); + return result; + } + + private Javac9BasedLombokOptions(Context context) { + super(context); + } + + @Override public void putJavacOption(String optionName, String value) { + if (optionName.equals("CLASSPATH")) optionName = "CLASS_PATH"; + if (optionName.equals("SOURCEPATH")) optionName = "SOURCE_PATH"; + if (optionName.equals("BOOTCLASSPATH")) optionName = "BOOT_CLASS_PATH"; + String optionText = Option.valueOf(optionName).primaryName; + put(optionText, value); + } +} diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java index c99ae5c9..45679fd3 100644 --- a/src/core/lombok/javac/JavacAST.java +++ b/src/core/lombok/javac/JavacAST.java @@ -48,8 +48,6 @@ import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCExpression; -import com.sun.tools.javac.tree.JCTree.JCFieldAccess; -import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTry; @@ -65,12 +63,12 @@ import com.sun.tools.javac.util.Name; * something javac's own AST system does not offer. */ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { - private final Messager messager; private final JavacElements elements; private final JavacTreeMaker treeMaker; private final Symtab symtab; private final JavacTypes javacTypes; private final Log log; + private final ErrorLog errorLogger; private final Context context; /** @@ -84,8 +82,8 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { super(sourceName(top), PackageName.getPackageName(top), new JavacImportList(top), statementTypes()); setTop(buildCompilationUnit(top)); this.context = context; - this.messager = messager; this.log = Log.instance(context); + this.errorLogger = ErrorLog.create(messager, log); this.elements = JavacElements.instance(context); this.treeMaker = new JavacTreeMaker(TreeMaker.instance(context)); this.symtab = Symtab.instance(context); @@ -106,27 +104,6 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { return cu.sourcefile == null ? null : cu.sourcefile.toString(); } - // jdk9 support, types have changed, names stay the same - static class PackageName { - private static final Method packageNameMethod; - - static { - Method m = null; - try { - m = JCCompilationUnit.class.getDeclaredMethod("getPackageName"); - } catch (Exception e) {} - packageNameMethod = m; - } - - static String getPackageName(JCCompilationUnit cu) { - try { - Object pkg = packageNameMethod.invoke(cu); - return (pkg instanceof JCFieldAccess || pkg instanceof JCIdent) ? pkg.toString() : null; - } catch (Exception e) {} - return null; - } - } - public Context getContext() { return context; } @@ -148,6 +125,8 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { String nm = Source.instance(context).name(); int underscoreIdx = nm.indexOf('_'); if (underscoreIdx > -1) return Integer.parseInt(nm.substring(underscoreIdx + 1)); + // assume java9+ + return Integer.parseInt(nm); } catch (Exception ignore) {} return 6; } @@ -422,22 +401,21 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { try { switch (kind) { case ERROR: - increaseErrorCount(messager); - boolean prev = log.multipleErrors; - log.multipleErrors = true; - try { - log.error(pos, "proc.messager", message); - } finally { - log.multipleErrors = prev; - } + errorLogger.error(pos, message); + break; + case MANDATORY_WARNING: + errorLogger.mandatoryWarning(pos, message); break; - default: case WARNING: - log.warning(pos, "proc.messager", message); + errorLogger.warning(pos, message); + break; + default: + case NOTE: + errorLogger.note(pos, message); break; } } finally { - if (oldSource != null) log.useSource(oldSource); + if (newSource != null) log.useSource(oldSource); } } @@ -475,16 +453,118 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { return oldL; } - private void increaseErrorCount(Messager m) { - try { - Field f = m.getClass().getDeclaredField("errorCount"); - f.setAccessible(true); - if (f.getType() == int.class) { - int val = ((Number)f.get(m)).intValue(); - f.set(m, val +1); + abstract static class ErrorLog { + final Log log; + private final Messager messager; + private final Field errorCount; + private final Field warningCount; + + private ErrorLog(Log log, Messager messager, Field errorCount, Field warningCount) { + this.log = log; + this.messager = messager; + this.errorCount = errorCount; + this.warningCount = warningCount; + } + + final void error(DiagnosticPosition pos, String message) { + increment(errorCount); + error1(pos, message); + } + + final void warning(DiagnosticPosition pos, String message) { + increment(warningCount); + log.warning(pos, "proc.messager", message); + } + + final void mandatoryWarning(DiagnosticPosition pos, String message) { + increment(warningCount); + log.mandatoryWarning(pos, "proc.messager", message); + } + + final void note(DiagnosticPosition pos, String message) { + log.note(pos, "proc.messager", message); + } + + abstract void error1(DiagnosticPosition pos, String message); + + private void increment(Field field) { + if (field == null) return; + try { + int val = ((Number)field.get(messager)).intValue(); + field.set(messager, val +1); + } catch (Throwable t) { + //Very unfortunate, but in most cases it still works fine, so we'll silently swallow it. + } + } + + static ErrorLog create(Messager messager, Log log) { + Field errorCount = null; + try { + Field f = messager.getClass().getDeclaredField("errorCount"); + f.setAccessible(true); + errorCount = f; + } catch (Throwable t) {} + boolean hasMultipleErrors = false; + for (Field field : log.getClass().getFields()) { + if (field.getName().equals("multipleErrors")) { + hasMultipleErrors = true; + break; + } + } + if (hasMultipleErrors) return new JdkBefore9(log, messager, errorCount); + + Field warningCount = null; + try { + Field f = messager.getClass().getDeclaredField("warningCount"); + f.setAccessible(true); + warningCount = f; + } catch (Throwable t) {} + + + Method logMethod = null; + Object multiple = null; + try { + Class<?> df = Class.forName("com.sun.tools.javac.util.JCDiagnostic$DiagnosticFlag"); + for (Object constant : df.getEnumConstants()) { + if (constant.toString().equals("MULTIPLE")) multiple = constant; + } + logMethod = log.getClass().getMethod("error", new Class<?>[] {df, DiagnosticPosition.class, String.class, Object[].class}); + } catch (Throwable t) {} + + return new Jdk9Plus(log, messager, errorCount, warningCount, logMethod, multiple); + } + } + + static class JdkBefore9 extends ErrorLog { + private JdkBefore9(Log log, Messager messager, Field errorCount) { + super(log, messager, errorCount, null); + } + + @Override void error1(DiagnosticPosition pos, String message) { + boolean prev = log.multipleErrors; + log.multipleErrors = true; + try { + log.error(pos, "proc.messager", message); + } finally { + log.multipleErrors = prev; } - } catch (Throwable t) { - //Very unfortunate, but in most cases it still works fine, so we'll silently swallow it. + } + } + + static class Jdk9Plus extends ErrorLog { + private final Object multiple; + private final Method logMethod; + + private Jdk9Plus(Log log, Messager messager, Field errorCount, Field warningCount, Method logMethod, Object multiple) { + super(log, messager, errorCount, warningCount); + this.logMethod = logMethod; + this.multiple = multiple; + } + + @Override void error1(DiagnosticPosition pos, String message) { + try { + logMethod.invoke(log, multiple, pos, "proc.messager", new Object[] { message }); + } catch (Throwable t) {} } } } diff --git a/src/core/lombok/javac/JavacImportList.java b/src/core/lombok/javac/JavacImportList.java index 0f789f45..468d8c7b 100644 --- a/src/core/lombok/javac/JavacImportList.java +++ b/src/core/lombok/javac/JavacImportList.java @@ -26,7 +26,6 @@ import java.util.Collection; import lombok.core.ImportList; import lombok.core.LombokInternalAliasing; -import lombok.javac.JavacAST.PackageName; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java index 67dbaac6..8cc239e1 100644 --- a/src/core/lombok/javac/JavacResolution.java +++ b/src/core/lombok/javac/JavacResolution.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 The Project Lombok Authors. + * Copyright (C) 2011-2018 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 java.util.ArrayDeque; import java.util.Map; import javax.lang.model.type.TypeKind; +import javax.tools.JavaFileObject; import lombok.Lombok; import lombok.core.debug.AssertionLogger; @@ -59,6 +60,7 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Log; public class JavacResolution { private final Attr attr; @@ -142,9 +144,14 @@ public class JavacResolution { TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node.getTreeMaker(), node.getContext()); JCTree copy = mirrorMaker.copy(finder.copyAt()); - - memberEnterAndAttribute(copy, finder.get(), node.getContext()); - return mirrorMaker.getOriginalToCopyMap(); + Log log = Log.instance(node.getContext()); + JavaFileObject oldFileObject = log.useSource(((JCCompilationUnit) node.top().get()).getSourceFile()); + try { + memberEnterAndAttribute(copy, finder.get(), node.getContext()); + return mirrorMaker.getOriginalToCopyMap(); + } finally { + log.useSource(oldFileObject); + } } finally { messageSuppressor.enableLoggers(); } @@ -222,8 +229,13 @@ public class JavacResolution { } private void attrib(JCTree tree, Env<AttrContext> env) { + if (env.enclClass.type == null) try { + env.enclClass.type = Type.noType; + } catch (Throwable ignore) { + // This addresses issue #1553 which involves JDK9; if it doesn't exist, we probably don't need to set it. + } if (tree instanceof JCBlock) attr.attribStat(tree, env); - else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl)tree).body, env); + else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl) tree).body, env); else if (tree instanceof JCVariableDecl) attr.attribStat(tree, env); else throw new IllegalStateException("Called with something that isn't a block, method decl, or variable decl"); } @@ -261,6 +273,7 @@ public class JavacResolution { } public static Type ifTypeIsIterableToComponent(Type type, JavacAST ast) { + if (type == null) return null; Types types = Types.instance(ast.getContext()); Symtab syms = Symtab.instance(ast.getContext()); Type boundType = ReflectiveAccess.Types_upperBound(types, type); diff --git a/src/core/lombok/javac/apt/InterceptingJavaFileManager.java b/src/core/lombok/javac/apt/InterceptingJavaFileManager.java index 303bdc2f..9b58d111 100644 --- a/src/core/lombok/javac/apt/InterceptingJavaFileManager.java +++ b/src/core/lombok/javac/apt/InterceptingJavaFileManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2011 The Project Lombok Authors. + * Copyright (C) 2010-2018 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 @@ -42,14 +42,14 @@ final class InterceptingJavaFileManager extends ForwardingJavaFileManager<JavaFi } @Override public JavaFileObject getJavaFileForOutput(Location location, String className, final Kind kind, FileObject sibling) throws IOException { - if (className.startsWith("lombok.dummy.ForceNewRound")) { + if (className.contains("lombok.dummy.ForceNewRound")) { final String name = className.replace(".", "/") + kind.extension; return LombokFileObjects.createEmpty(compiler, name, kind); } + JavaFileObject fileObject = fileManager.getJavaFileForOutput(location, className, kind, sibling); - if (kind != Kind.CLASS) { - return fileObject; - } + if (kind != Kind.CLASS) return fileObject; + return LombokFileObjects.createIntercepting(compiler, fileObject, className, diagnostics); } }
\ No newline at end of file diff --git a/src/core/lombok/javac/apt/LombokFileObjects.java b/src/core/lombok/javac/apt/LombokFileObjects.java index 7e818cab..aba10540 100644 --- a/src/core/lombok/javac/apt/LombokFileObjects.java +++ b/src/core/lombok/javac/apt/LombokFileObjects.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2017 The Project Lombok Authors. + * Copyright (C) 2010-2018 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,19 +22,26 @@ package lombok.javac.apt; +import java.io.IOException; import java.lang.reflect.Method; import java.net.URI; +import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import javax.tools.FileObject; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; -import com.sun.tools.javac.file.BaseFileManager; - import lombok.core.DiagnosticsReceiver; +import com.sun.tools.javac.file.BaseFileManager; + //Can't use SimpleJavaFileObject so we copy/paste most of its content here, because javac doesn't follow the interface, //and casts to its own BaseFileObject type. D'oh! final class LombokFileObjects { @@ -94,6 +101,15 @@ final class LombokFileObjects { private LombokFileObjects() {} + private static final List<String> KNOWN_JAVA9_FILE_MANAGERS = Arrays.asList( + "com.google.errorprone.MaskedClassLoader$MaskedFileManager", + "com.google.devtools.build.buildjar.javac.BlazeJavacMain$ClassloaderMaskingFileManager", + "com.google.devtools.build.java.turbine.javac.JavacTurbineCompiler$ClassloaderMaskingFileManager", + "org.netbeans.modules.java.source.parsing.ProxyFileManager", + "com.sun.tools.javac.api.ClientCodeWrapper$WrappedStandardJavaFileManager", + "com.sun.tools.javac.main.DelegatingJavaFileManager$DelegatingSJFM" // IntelliJ + JDK10 + ); + static Compiler getCompiler(JavaFileManager jfm) { String jfmClassName = jfm != null ? jfm.getClass().getName() : "null"; if (jfmClassName.equals("com.sun.tools.javac.util.DefaultFileManager")) return Compiler.JAVAC6; @@ -105,18 +121,36 @@ final class LombokFileObjects { return new Java9Compiler(jfm); } } - catch (Exception e) {} + catch (Throwable e) {} return Compiler.JAVAC7; } + if (KNOWN_JAVA9_FILE_MANAGERS.contains(jfmClassName)) { + try { + return new Java9Compiler(jfm); + } + catch (Throwable e) {} + } + try { + if (Class.forName("com.sun.tools.javac.file.PathFileObject") == null) throw new NullPointerException(); + return new Java9Compiler(jfm); + } catch (Throwable e) {} try { if (Class.forName("com.sun.tools.javac.file.BaseFileObject") == null) throw new NullPointerException(); return Compiler.JAVAC7; - } catch (Exception e) {} + } catch (Throwable e) {} try { if (Class.forName("com.sun.tools.javac.util.BaseFileObject") == null) throw new NullPointerException(); return Compiler.JAVAC6; - } catch (Exception e) {} - return null; + } catch (Throwable e) {} + + StringBuilder sb = new StringBuilder(jfmClassName); + if (jfm != null) { + sb.append(" extends ").append(jfm.getClass().getSuperclass().getName()); + for (Class<?> cls : jfm.getClass().getInterfaces()) { + sb.append(" implements ").append(cls.getName()); + } + } + throw new IllegalArgumentException(sb.toString()); } static JavaFileObject createEmpty(Compiler compiler, String name, Kind kind) { @@ -131,19 +165,108 @@ final class LombokFileObjects { private final BaseFileManager fileManager; public Java9Compiler(JavaFileManager jfm) { - fileManager = (BaseFileManager) jfm; + fileManager = asBaseFileManager(jfm); } @Override public JavaFileObject wrap(LombokFileObject fileObject) { + return new Javac9BaseFileObjectWrapper(fileManager, toPath(fileObject), fileObject); + } + + @Override public Method getDecoderMethod() { + return null; + } + + private static Path toPath(LombokFileObject fileObject) { URI uri = fileObject.toUri(); if (uri.getScheme() == null) { - uri = URI.create("file://" + uri); + uri = URI.create("file:///" + uri); + } + try { + return Paths.get(uri); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Problems in URI '" + uri + "' (" + fileObject.toUri() + ")", e); } - return new Javac9BaseFileObjectWrapper(fileManager, Paths.get(uri), fileObject); } - @Override public Method getDecoderMethod() { - throw new UnsupportedOperationException(); + private static BaseFileManager asBaseFileManager(JavaFileManager jfm) { + if (jfm instanceof BaseFileManager) { + return (BaseFileManager) jfm; + } + return new FileManagerWrapper(jfm); + } + + static class FileManagerWrapper extends BaseFileManager { + JavaFileManager manager; + + public FileManagerWrapper(JavaFileManager manager) { + super(null); + this.manager = manager; + } + + @Override + public int isSupportedOption(String option) { + return manager.isSupportedOption(option); + } + + @Override + public ClassLoader getClassLoader(Location location) { + return manager.getClassLoader(location); + } + + @Override + public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException { + return manager.list(location, packageName, kinds, recurse); + } + + @Override + public String inferBinaryName(Location location, JavaFileObject file) { + return manager.inferBinaryName(location, file); + } + + @Override + public boolean isSameFile(FileObject a, FileObject b) { + return manager.isSameFile(a, b); + } + + @Override + public boolean handleOption(String current, Iterator<String> remaining) { + return manager.handleOption(current, remaining); + } + + @Override + public boolean hasLocation(Location location) { + return manager.hasLocation(location); + } + + @Override + public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException { + return manager.getJavaFileForInput(location, className, kind); + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { + return manager.getJavaFileForOutput(location, className, kind, sibling); + } + + @Override + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { + return manager.getFileForInput(location, packageName, relativeName); + } + + @Override + public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException { + return manager.getFileForOutput(location, packageName, relativeName, sibling); + } + + @Override + public void flush() throws IOException { + manager.flush(); + } + + @Override + public void close() throws IOException { + manager.close(); + } } } } diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java index b962a955..9c0a2dfa 100644 --- a/src/core/lombok/javac/apt/LombokProcessor.java +++ b/src/core/lombok/javac/apt/LombokProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2018 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 @@ -54,6 +54,8 @@ import lombok.javac.JavacTransformer; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; +import com.sun.tools.javac.jvm.ClassWriter; +import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.processing.JavacFiler; import com.sun.tools.javac.processing.JavacProcessingEnvironment; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; @@ -156,7 +158,6 @@ public class LombokProcessor extends AbstractProcessor { @SuppressWarnings("unchecked") Map<Object,Object> ht = (Map<Object,Object>) htField.get(context); final JavaFileManager originalFiler = (JavaFileManager) ht.get(key); - if (!(originalFiler instanceof InterceptingJavaFileManager)) { final Messager messager = processingEnv.getMessager(); DiagnosticsReceiver receiver = new MessagerDiagnosticsReceiver(messager); @@ -166,11 +167,37 @@ public class LombokProcessor extends AbstractProcessor { Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager"); filerFileManagerField.setAccessible(true); filerFileManagerField.set(processingEnv.getFiler(), newFiler); + + replaceFileManagerJdk9(context, newFiler); } } catch (Exception e) { throw Lombok.sneakyThrow(e); } } + + private void replaceFileManagerJdk9(Context context, JavaFileManager newFiler) { + try { + JavaCompiler compiler = (JavaCompiler) JavaCompiler.class.getDeclaredMethod("instance", Context.class).invoke(null, context); + try { + Field fileManagerField = JavaCompiler.class.getDeclaredField("fileManager"); + fileManagerField.setAccessible(true); + fileManagerField.set(compiler, newFiler); + } + catch (Exception e) {} + + try { + Field writerField = JavaCompiler.class.getDeclaredField("writer"); + writerField.setAccessible(true); + ClassWriter writer = (ClassWriter) writerField.get(compiler); + Field fileManagerField = ClassWriter.class.getDeclaredField("fileManager"); + fileManagerField.setAccessible(true); + fileManagerField.set(writer, newFiler); + } + catch (Exception e) {} + } + catch (Exception e) { + } + } private void forceMultipleRoundsInNetBeansEditor() { try { @@ -274,7 +301,10 @@ public class LombokProcessor extends AbstractProcessor { // Step 1: Take all CUs which aren't already in the map. Give them the first priority level. + String randomModuleName = null; + for (Element element : roundEnv.getRootElements()) { + if (randomModuleName == null) randomModuleName = getModuleNameFor(element); JCCompilationUnit unit = toUnit(element); if (unit == null) continue; if (roots.containsKey(unit)) continue; @@ -317,20 +347,22 @@ public class LombokProcessor extends AbstractProcessor { if (newLevels.isEmpty()) return false; newLevels.retainAll(priorityLevelsRequiringResolutionReset); - if (!newLevels.isEmpty()){ + if (!newLevels.isEmpty()) { // Force a new round to reset resolution. The next round will cause this method (process) to be called again. - forceNewRound((JavacFiler) processingEnv.getFiler()); + forceNewRound(randomModuleName, (JavacFiler) processingEnv.getFiler()); return false; } - // None of the new levels need resolution, so just keep going. + // None of the new levels need resolution, so just keep going. } } private int dummyCount = 0; - private void forceNewRound(JavacFiler filer) { + private void forceNewRound(String randomModuleName, JavacFiler filer) { if (!filer.newFiles()) { try { - JavaFileObject dummy = filer.createSourceFile("lombok.dummy.ForceNewRound" + (dummyCount++)); + String name = "lombok.dummy.ForceNewRound" + (dummyCount++); + if (randomModuleName != null) name = randomModuleName + "/" + name; + JavaFileObject dummy = filer.createSourceFile(name); Writer w = dummy.openWriter(); w.close(); } catch (Exception e) { @@ -341,6 +373,19 @@ public class LombokProcessor extends AbstractProcessor { } } + private String getModuleNameFor(Element element) { + while (element != null) { + if (element.getKind().name().equals("MODULE")) { + String n = element.getSimpleName().toString().trim(); + return n.isEmpty() ? null : n; + } + Element n = element.getEnclosingElement(); + if (n == element) return null; + element = n; + } + return null; + } + private JCCompilationUnit toUnit(Element element) { TreePath path = trees == null ? null : trees.getPath(element); if (path == null) return null; diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 9860fb07..4c575d91 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2015 The Project Lombok Authors. + * Copyright (C) 2013-2018 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,7 +21,6 @@ */ package lombok.javac.handlers; -import java.lang.annotation.Annotation; import java.util.ArrayList; import javax.lang.model.element.Modifier; @@ -76,6 +75,8 @@ import static lombok.javac.JavacTreeMaker.TypeTag.*; @ProviderFor(JavacAnnotationHandler.class) @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(); + private static final boolean toBoolean(Object expr, boolean defaultValue) { if (expr == null) return defaultValue; if (expr instanceof JCLiteral) return ((Integer) ((JCLiteral) expr).value) != 0; @@ -87,9 +88,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JCExpression type; Name rawName; Name name; + Name nameOfDefaultProvider; + Name nameOfSetFlag; SingularData singularData; ObtainVia obtainVia; JavacNode obtainViaNode; + JavacNode originalFieldNode; java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>(); } @@ -107,18 +111,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { String toBuilderMethodName = "toBuilder"; boolean inherit = builderInstance.inherit(); - boolean extendable = inherit || builderInstance.extendable(); // inherit implies extendable - String superclassBuilderClassName = builderInstance.superclassBuilderClassName(); - + boolean extensible = inherit || builderInstance.extensible(); // inherit implies extendable + boolean toBuilder = builderInstance.toBuilder(); java.util.List<Name> typeArgsForToBuilder = null; if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; if (builderClassName == null) builderClassName = ""; - if (superclassBuilderClassName == null) { - superclassBuilderClassName = ""; - } if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; @@ -126,9 +126,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (!checkName("builderClassName", builderClassName, annotationNode)) return; } - @SuppressWarnings("deprecation") - Class<? extends Annotation> oldExperimentalBuilder = lombok.experimental.Builder.class; - deleteAnnotationIfNeccessary(annotationNode, Builder.class, oldExperimentalBuilder); + deleteAnnotationIfNeccessary(annotationNode, Builder.class, "lombok.experimental.Builder"); JavacNode parent = annotationNode.up(); @@ -143,43 +141,61 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { boolean addCleaning = false; boolean isStatic = true; + JCClassDecl td = null; + if (parent.get() instanceof JCClassDecl) { tdParent = parent; - JCClassDecl td = (JCClassDecl) tdParent.get(); + td = (JCClassDecl) tdParent.get(); ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>(); - @SuppressWarnings("deprecation") - boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent)); - for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) { + boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); + for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { JCVariableDecl fd = (JCVariableDecl) fieldNode.get(); - // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes - // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves. - // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that. - if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; + JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, true); + boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); BuilderFieldData bfd = new BuilderFieldData(); bfd.fieldNode = fieldNode; bfd.rawName = fd.name; bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.vartype; bfd.singularData = getSingularData(fieldNode); + bfd.originalFieldNode = fieldNode; + + if (bfd.singularData != null && isDefault != null) { + isDefault.addError("@Builder.Default and @Singular cannot be mixed."); + isDefault = null; + } + + if (fd.init == null && isDefault != null) { + isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;')."); + isDefault = null; + } + + if (fd.init != null && isDefault == null) { + if (isFinal) continue; + fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + } + + if (isDefault != null) { + bfd.nameOfDefaultProvider = parent.toName("$default$" + bfd.name); + bfd.nameOfSetFlag = parent.toName(bfd.name + "$set"); + JCMethodDecl md = generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode, td.typarams); + recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); + if (md != null) injectMethod(tdParent, md); + } addObtainVia(bfd, fieldNode); builderFields.add(bfd); allFields.append(fieldNode); } - if (builderClassName.isEmpty()) { - builderClassName = td.name.toString() + "Builder"; - } + if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; JCTree extendsClause = Javac.getExtendsClause(td); - if (superclassBuilderClassName.isEmpty() && extendsClause != null) { - superclassBuilderClassName = extendsClause + "Builder"; - } - if (extendable) { + if (extensible) { boolean callBuilderBasedSuperConstructor = extendsClause != null; generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, callBuilderBasedSuperConstructor); } else { - new HandleConstructor().generateConstructor(tdParent, AccessLevel.PROTECTED, List.<JCAnnotation>nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); + handleConstructor.generateConstructor(tdParent, AccessLevel.PROTECTED, List.<JCAnnotation>nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); } returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); @@ -191,7 +207,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); return; } - if (extendable) { + if (extensible) { annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); return; } @@ -202,7 +218,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } tdParent = parent.up(); - JCClassDecl td = (JCClassDecl) tdParent.get(); + td = (JCClassDecl) tdParent.get(); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = jmd.thrown; @@ -213,12 +229,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); return; } - if (extendable) { + if (extensible) { annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); return; } tdParent = parent.up(); - JCClassDecl td = (JCClassDecl) tdParent.get(); + td = (JCClassDecl) tdParent.get(); JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); isStatic = (jmd.mods.flags & Flags.STATIC) != 0; JCExpression fullReturnType = jmd.restype; @@ -227,7 +243,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { thrownExceptions = jmd.thrown; nameOfBuilderMethod = jmd.name; if (returnType instanceof JCTypeApply) { - returnType = ((JCTypeApply) returnType).clazz; + returnType = cloneType(tdParent.getTreeMaker(), returnType, ast, annotationNode.getContext()); } if (builderClassName.isEmpty()) { if (returnType instanceof JCFieldAccess) { @@ -247,7 +263,16 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (Character.isLowerCase(builderClassName.charAt(0))) { builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1); } - } else { + } else if (returnType instanceof JCTypeApply) { + JCExpression clazz = ((JCTypeApply) returnType).clazz; + if (clazz instanceof JCFieldAccess) { + builderClassName = ((JCFieldAccess) clazz).name + "Builder"; + } else if (clazz instanceof JCIdent) { + builderClassName = ((JCIdent) clazz).name + "Builder"; + } + } + + if (builderClassName.isEmpty()) { // This shouldn't happen. System.err.println("Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass()); builderClassName = td.name.toString() + "Builder"; @@ -268,11 +293,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { tpOnRet = ((JCTypeApply) fullReturnType).arguments; } - if (returnType instanceof JCIdent) { - simpleName = ((JCIdent) returnType).name; + JCExpression namingType = returnType; + if (returnType instanceof JCTypeApply) namingType = ((JCTypeApply) returnType).clazz; + + if (namingType instanceof JCIdent) { + simpleName = ((JCIdent) namingType).name; pkg = null; - } else if (returnType instanceof JCFieldAccess) { - JCFieldAccess jcfa = (JCFieldAccess) returnType; + } else if (namingType instanceof JCFieldAccess) { + JCFieldAccess jcfa = (JCFieldAccess) namingType; simpleName = jcfa.name; pkg = unpack(jcfa.selected); if (pkg.startsWith("ERR:")) { @@ -281,7 +309,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { return; } } else { - annotationNode.addError("Expected a (parameterized) type here instead of a " + returnType.getClass().getName()); + annotationNode.addError("Expected a (parameterized) type here instead of a " + namingType.getClass().getName()); return; } @@ -330,6 +358,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { bfd.rawName = raw.name; bfd.type = raw.vartype; bfd.singularData = getSingularData(param); + bfd.originalFieldNode = param; addObtainVia(bfd, param); builderFields.add(bfd); } @@ -337,7 +366,18 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(isStatic, annotationNode, tdParent, builderClassName, typeParams, ast, inherit ? superclassBuilderClassName : null); + String superclassBuilderClassName = null; + if (inherit) { + JCTree extendsClause = Javac.getExtendsClause(td); + if (extendsClause == null) { + annotationNode.addError("@Builder(inherit = true) requires that your class has an 'extends' clause."); + return; + } + + superclassBuilderClassName = builderInstance.superclassBuilderClassName(); + if (superclassBuilderClassName == null || superclassBuilderClassName.isEmpty()) superclassBuilderClassName = extendsClause + "Builder"; + } + builderType = makeBuilderClass(isStatic, annotationNode, tdParent, builderClassName, typeParams, ast, superclassBuilderClassName); } else { JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get(); if (isStatic && !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) { @@ -397,8 +437,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - boolean useBuilderBasedConstructor = parent.get() instanceof JCClassDecl && extendable; - JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning, useBuilderBasedConstructor); + JCMethodDecl md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning, extensible); if (md != null) injectMethod(builderType, md); } @@ -528,17 +567,10 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { * {@code builderClassAsParameter != null}. */ private void generateBuilderBasedConstructor(JavacNode typeNode, java.util.List<BuilderFieldData> builderFields, JavacNode source, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { - if (builderClassnameAsParameter == null || builderClassnameAsParameter.isEmpty()) { - source.addError("A builder-based constructor requires a non-empty 'builderClassnameAsParameter' value."); - } - JavacTreeMaker maker = typeNode.getTreeMaker(); - AccessLevel level = AccessLevel.PROTECTED; boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0; - if (isEnum) { - level = AccessLevel.PRIVATE; - } + AccessLevel level = isEnum ? AccessLevel.PRIVATE : AccessLevel.PROTECTED; ListBuffer<JCStatement> nullChecks = new ListBuffer<JCStatement>(); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); @@ -583,11 +615,11 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { List.<JCExpression>of(maker.Ident(builderVariableName))); statements.prepend(maker.Exec(callToSuperConstructor)); } - + JCMethodDecl constr = recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"), null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), maker.Block(0L, nullChecks.appendList(statements).toList()), null), source.get(), typeNode.getContext()); - + injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); } @@ -601,7 +633,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } } - statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, false)))); + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0)))); JCBlock body = maker.Block(0, statements.toList()); return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(type.getSymbolTable(), CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); /* @@ -624,13 +656,13 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { * that takes the builder instance as parameter (instead of a * constructor with all relevant fields as parameters) */ - private JCMethodDecl generateBuildMethod(boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning, boolean useBuilderBasedConstructor) { + private JCMethodDecl generateBuildMethod(JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning, boolean useBuilderBasedConstructor) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); - + // Extendable builders assign their values in the constructor, not in this build() method. if (!useBuilderBasedConstructor) { if (addCleaning) { @@ -645,13 +677,17 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name, "this"); } } - + for (BuilderFieldData bfd : builderFields) { + if (bfd.nameOfSetFlag != null) { + statements.append(maker.VarDef(maker.Modifiers(0L), bfd.name, cloneType(maker, bfd.type, source, tdParent.getContext()), maker.Select(maker.Ident(type.toName("this")), bfd.name))); + statements.append(maker.If(maker.Unary(CTC_NOT, maker.Ident(bfd.nameOfSetFlag)), maker.Exec(maker.Assign(maker.Ident(bfd.name),maker.Apply(typeParameterNames(maker, ((JCClassDecl) tdParent.get()).typarams), maker.Select(maker.Ident(((JCClassDecl) tdParent.get()).name), bfd.nameOfDefaultProvider), List.<JCExpression>nil()))), null)); + } args.append(maker.Ident(bfd.name)); } if (addCleaning) { - statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true)))); + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 1)))); } } @@ -666,14 +702,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } statements.append(maker.Return(call)); } else { - ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>(); for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) { typeParams.append(maker.Ident(tp.name)); } JCExpression callee = maker.Ident(((JCClassDecl) type.up().get()).name); - if (!isStatic) - callee = maker.Select(callee, type.up().toName("this")); + if (!isStatic) callee = maker.Select(callee, type.up().toName("this")); JCExpression fn = maker.Select(callee, builderName); call = maker.Apply(typeParams.toList(), fn, args.toList()); if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) { @@ -688,6 +722,18 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null); } + public JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode, List<JCTypeParameter> params) { + JavacTreeMaker maker = fieldNode.getTreeMaker(); + JCVariableDecl field = (JCVariableDecl) fieldNode.get(); + + JCStatement statement = maker.Return(field.init); + field.init = null; + + JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); + int modifiers = Flags.PRIVATE | Flags.STATIC; + return maker.MethodDef(maker.Modifiers(modifiers), methodName, cloneType(maker, field.vartype, field, fieldNode.getContext()), copyTypeParams(fieldNode, params), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); + } + public JCMethodDecl generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) { JavacTreeMaker maker = type.getTreeMaker(); @@ -712,36 +758,43 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (child.getKind() == Kind.FIELD) existing.add(child); } - top: for (int i = len - 1; i >= 0; i--) { BuilderFieldData bfd = builderFields.get(i); if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source)); } else { + JavacNode field = null, setFlag = null; for (JavacNode exists : existing) { Name n = ((JCVariableDecl) exists.get()).name; - if (n.equals(bfd.name)) { - bfd.createdFields.add(exists); - continue top; - } + if (n.equals(bfd.name)) field = exists; + if (n.equals(bfd.nameOfSetFlag)) setFlag = exists; } JavacTreeMaker maker = builderType.getTreeMaker(); - JCModifiers mods = maker.Modifiers(Flags.PRIVATE); - JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null); - bfd.createdFields.add(injectFieldAndMarkGenerated(builderType, newField)); + if (field == null) { + JCModifiers mods = maker.Modifiers(Flags.PRIVATE); + JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null); + field = injectFieldAndMarkGenerated(builderType, newField); + } + if (setFlag == null && bfd.nameOfSetFlag != null) { + JCModifiers mods = maker.Modifiers(Flags.PRIVATE); + JCVariableDecl newField = maker.VarDef(mods, bfd.nameOfSetFlag, maker.TypeIdent(CTC_BOOLEAN), null); + injectFieldAndMarkGenerated(builderType, newField); + } + bfd.createdFields.add(field); } } } public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain) { + boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, fieldNode.createdFields.get(0), source, fluent, chain); + makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, fluent, chain); } else { - fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, builderType, source.get(), fluent, chain); + fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain); } } - private void makeSimpleSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JavacNode source, boolean fluent, boolean chain) { + private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain) { Name fieldName = ((JCVariableDecl) fieldNode.get()).name; for (JavacNode child : builderType.down()) { @@ -754,7 +807,9 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); JavacTreeMaker maker = fieldNode.getTreeMaker(); - JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil()); + + JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil()); + injectMethod(builderType, newMethod); } @@ -772,10 +827,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { int modifiers = Flags.PUBLIC; if (isStatic) modifiers |= Flags.STATIC; JCModifiers mods = maker.Modifiers(modifiers); - JCExpression extending = null; - if (parentBuilderClassName != null) { - extending = maker.Ident(tdParent.toName(parentBuilderClassName)); - } + JCExpression extending = parentBuilderClassName == null ? null : maker.Ident(tdParent.toName(parentBuilderClassName)); JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(source, typeParams), extending, List.<JCExpression>nil(), List.<JCTree>nil()); return injectType(tdParent, builder); } diff --git a/src/core/lombok/javac/handlers/HandleBuilderDefault.java b/src/core/lombok/javac/handlers/HandleBuilderDefault.java new file mode 100644 index 00000000..4c4ba0e8 --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleBuilderDefault.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017-2018 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 static lombok.javac.handlers.JavacHandlerUtil.*; + +import org.mangosdk.spi.ProviderFor; + +import com.sun.tools.javac.tree.JCTree.JCAnnotation; + +import lombok.Builder; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; + +@ProviderFor(JavacAnnotationHandler.class) +@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) { + JavacNode annotatedField = annotationNode.up(); + if (annotatedField.getKind() != Kind.FIELD) return; + JavacNode classWithAnnotatedField = annotatedField.up(); + if (!hasAnnotation(Builder.class, classWithAnnotatedField) && !hasAnnotation("lombok.experimental.Builder", classWithAnnotatedField)) { + annotationNode.addWarning("@Builder.Default requires @Builder on the class for it to mean anything."); + deleteAnnotationIfNeccessary(annotationNode, Builder.Default.class); + } + } +} diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index 4e90b639..dca25ee7 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -64,6 +64,8 @@ import com.sun.tools.javac.util.Name; public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) public static class HandleNoArgsConstructor extends JavacAnnotationHandler<NoArgsConstructor> { + private HandleConstructor handleConstructor = new HandleConstructor(); + @Override public void handle(AnnotationValues<NoArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.NO_ARGS_CONSTRUCTOR_FLAG_USAGE, "@NoArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); @@ -78,12 +80,14 @@ public class HandleConstructor { String staticName = ann.staticName(); boolean force = ann.force(); List<JavacNode> fields = force ? findFinalFields(typeNode) : List.<JavacNode>nil(); - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, annotationNode); + handleConstructor.generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, annotationNode); } } @ProviderFor(JavacAnnotationHandler.class) public static class HandleRequiredArgsConstructor extends JavacAnnotationHandler<RequiredArgsConstructor> { + private HandleConstructor handleConstructor = new HandleConstructor(); + @Override public void handle(AnnotationValues<RequiredArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.REQUIRED_ARGS_CONSTRUCTOR_FLAG_USAGE, "@RequiredArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); @@ -100,7 +104,7 @@ public class HandleConstructor { annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'."); } - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode); + handleConstructor.generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode); } } @@ -131,6 +135,8 @@ public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) public static class HandleAllArgsConstructor extends JavacAnnotationHandler<AllArgsConstructor> { + private HandleConstructor handleConstructor = new HandleConstructor(); + @Override public void handle(AnnotationValues<AllArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.ALL_ARGS_CONSTRUCTOR_FLAG_USAGE, "@AllArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); @@ -146,11 +152,15 @@ public class HandleConstructor { if (annotation.isExplicit("suppressConstructorProperties")) { annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'."); } - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode); + handleConstructor.generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode); } } public static List<JavacNode> findAllFields(JavacNode typeNode) { + return findAllFields(typeNode, false); + } + + public static List<JavacNode> findAllFields(JavacNode typeNode, boolean evenFinalInitialized) { ListBuffer<JavacNode> fields = new ListBuffer<JavacNode>(); for (JavacNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; @@ -162,7 +172,7 @@ public class HandleConstructor { if ((fieldFlags & Flags.STATIC) != 0) continue; //Skip initialized final fields boolean isFinal = (fieldFlags & Flags.FINAL) != 0; - if (!isFinal || fieldDecl.init == null) fields.append(child); + if (evenFinalInitialized || !isFinal || fieldDecl.init == null) fields.append(child); } return fields.toList(); } @@ -256,18 +266,20 @@ public class HandleConstructor { mods.annotations = mods.annotations.append(annotation); } - public static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean allToDefault, JavacNode source) { + @SuppressWarnings("deprecation") public static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean allToDefault, JavacNode source) { JavacTreeMaker maker = typeNode.getTreeMaker(); boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0; if (isEnum) level = AccessLevel.PRIVATE; - boolean suppressConstructorProperties; + boolean addConstructorProperties; if (fields.isEmpty()) { - suppressConstructorProperties = false; + addConstructorProperties = false; } else { - suppressConstructorProperties = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + 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<JCStatement> nullChecks = new ListBuffer<JCStatement>(); @@ -285,7 +297,7 @@ public class HandleConstructor { JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, nonNulls.appendList(nullables)), fieldName, field.vartype, null); params.append(param); if (!nonNulls.isEmpty()) { - JCStatement nullCheck = generateNullCheck(maker, fieldNode, source); + JCStatement nullCheck = generateNullCheck(maker, fieldNode, param, source); if (nullCheck != null) nullChecks.append(nullCheck); } } @@ -295,7 +307,7 @@ public class HandleConstructor { } JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil()); - if (!allToDefault && !suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) { + if (!allToDefault && addConstructorProperties && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) { addConstructorProperties(mods, typeNode, fields); } if (onConstructor != null) mods.annotations = mods.annotations.appendList(copyAnnotations(onConstructor)); diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java index 9ecf8754..15f1fd83 100644 --- a/src/core/lombok/javac/handlers/HandleData.java +++ b/src/core/lombok/javac/handlers/HandleData.java @@ -40,6 +40,12 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation; */ @ProviderFor(JavacAnnotationHandler.class) public class HandleData extends JavacAnnotationHandler<Data> { + private HandleConstructor handleConstructor = new HandleConstructor(); + private HandleGetter handleGetter = new HandleGetter(); + private HandleSetter handleSetter = new HandleSetter(); + private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode(); + private HandleToString handleToString = new HandleToString(); + @Override public void handle(AnnotationValues<Data> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.DATA_FLAG_USAGE, "@Data"); @@ -54,11 +60,10 @@ public class HandleData extends JavacAnnotationHandler<Data> { String staticConstructorName = annotation.getInstance().staticConstructor(); - // TODO move this to the end OR move it to the top in eclipse. - new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode); - new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); - new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); - new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); - new HandleToString().generateToStringForType(typeNode, annotationNode); + handleConstructor.generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode); + handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + handleSetter.generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode); + handleToString.generateToStringForType(typeNode, annotationNode); } } diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index 6df56ed6..d8bfd154 100644 --- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java @@ -121,7 +121,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas return; } - generateMethods(typeNode, source, null, null, null, false, FieldAccess.GETTER, List.<JCAnnotation>nil()); + Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS); + FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; + + generateMethods(typeNode, source, null, null, null, false, access, List.<JCAnnotation>nil()); } public void generateMethods(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes, @@ -255,20 +258,23 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext()); /* final int PRIME = X; */ { - if (!fields.isEmpty() || callSuper) { + if (!fields.isEmpty()) { statements.append(maker.VarDef(maker.Modifiers(finalFlag), primeName, maker.TypeIdent(CTC_INT), maker.Literal(HandlerUtil.primeForHashcode()))); } } - /* int result = 1; */ { - statements.append(maker.VarDef(maker.Modifiers(0), resultName, maker.TypeIdent(CTC_INT), maker.Literal(1))); - } - - if (callSuper) { - JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(), - maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")), - List.<JCExpression>nil()); - statements.append(createResultCalculation(typeNode, callToSuper)); + /* int result = ... */ { + final JCExpression init; + if (callSuper) { + /* ... super.hashCode(); */ + init = maker.Apply(List.<JCExpression>nil(), + maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")), + List.<JCExpression>nil()); + } else { + /* ... 1; */ + init = maker.Literal(1); + } + statements.append(maker.VarDef(maker.Modifiers(0), resultName, maker.TypeIdent(CTC_INT), init)); } Name dollar = typeNode.toName("$"); @@ -365,31 +371,44 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas public JCExpression createTypeReference(JavacNode type, boolean addWildcards) { java.util.List<String> list = new ArrayList<String>(); + java.util.List<Integer> genericsCount = addWildcards ? new ArrayList<Integer>() : null; + list.add(type.getName()); + if (addWildcards) genericsCount.add(((JCClassDecl) type.get()).typarams.size()); + boolean staticContext = (((JCClassDecl) type.get()).getModifiers().flags & Flags.STATIC) != 0; JavacNode tNode = type.up(); + while (tNode != null && tNode.getKind() == Kind.TYPE) { list.add(tNode.getName()); + if (addWildcards) genericsCount.add(staticContext ? 0 : ((JCClassDecl) tNode.get()).typarams.size()); + if (!staticContext) staticContext = (((JCClassDecl) tNode.get()).getModifiers().flags & Flags.STATIC) != 0; tNode = tNode.up(); } Collections.reverse(list); - JCClassDecl typeDecl = (JCClassDecl) type.get(); + if (addWildcards) Collections.reverse(genericsCount); JavacTreeMaker maker = type.getTreeMaker(); JCExpression chain = maker.Ident(type.toName(list.get(0))); + if (addWildcards) chain = wildcardify(maker, chain, genericsCount.get(0)); for (int i = 1; i < list.size(); i++) { chain = maker.Select(chain, type.toName(list.get(i))); + if (addWildcards) chain = wildcardify(maker, chain, genericsCount.get(i)); } - if (!addWildcards || typeDecl.typarams.length() == 0) return chain; + return chain; + } + + private JCExpression wildcardify(JavacTreeMaker maker, JCExpression expr, int count) { + if (count == 0) return expr; ListBuffer<JCExpression> wildcards = new ListBuffer<JCExpression>(); - for (int i = 0 ; i < typeDecl.typarams.length() ; i++) { + for (int i = 0 ; i < count ; i++) { wildcards.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null)); } - return maker.TypeApply(chain, wildcards.toList()); + return maker.TypeApply(expr, wildcards.toList()); } public JCMethodDecl createEquals(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual, JCTree source, List<JCAnnotation> onParam) { diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 5a2e1993..0540465d 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -95,7 +95,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { } } - public boolean fieldQualifiesForGetterGeneration(JavacNode field) { + public static boolean fieldQualifiesForGetterGeneration(JavacNode field) { if (field.getKind() != Kind.FIELD) return false; JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); //Skip fields that start with $ diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index e766127a..1453aa27 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -206,10 +206,10 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { String setterName = toSetterName(field); boolean returnThis = shouldReturnThis(field); - return createSetter(access, field, treeMaker, setterName, returnThis, source, onMethod, onParam); + return createSetter(access, false, field, treeMaker, setterName, null, returnThis, source, onMethod, onParam); } - public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, String setterName, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { + public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { if (setterName == null) return null; JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); @@ -235,6 +235,11 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { statements.append(treeMaker.Exec(assign)); } + if (booleanFieldToSet != null) { + JCAssign setBool = treeMaker.Assign(treeMaker.Ident(booleanFieldToSet), treeMaker.Literal(CTC_BOOLEAN, 1)); + statements.append(treeMaker.Exec(setBool)); + } + JCExpression methodType = null; if (shouldReturnThis) { methodType = cloneSelfType(field); @@ -258,7 +263,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { JCExpression annotationMethodDefaultValue = null; List<JCAnnotation> annsOnMethod = copyAnnotations(onMethod); - if (isFieldDeprecated(field)) { + if (isFieldDeprecated(field) || deprecate) { annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.<JCExpression>nil())); } diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index 743e7b26..897d5f2c 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -117,7 +117,11 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); includeFieldNames = configuration != null ? configuration : ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); } catch (Exception ignore) {} - generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, FieldAccess.GETTER); + + Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); + FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; + + generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, access); } public void generateToString(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes, diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 2976eabe..14130bc4 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2016 The Project Lombok Authors. + * Copyright (C) 2010-2018 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,7 @@ import static lombok.javac.handlers.JavacHandlerUtil.*; import lombok.ConfigurationKeys; import lombok.val; import lombok.core.HandlerPriority; -import lombok.experimental.var; +import lombok.var; import lombok.javac.JavacASTAdapter; import lombok.javac.JavacASTVisitor; import lombok.javac.JavacNode; @@ -54,29 +54,34 @@ import com.sun.tools.javac.util.List; public class HandleVal extends JavacASTAdapter { private static boolean eq(String typeTreeToString, String key) { - return (typeTreeToString.equals(key) || typeTreeToString.equals("lombok." + key)); + return typeTreeToString.equals(key) || typeTreeToString.equals("lombok." + key) || typeTreeToString.equals("lombok.experimental." + key); } - @Override + @SuppressWarnings("deprecation") @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) { JCTree typeTree = local.vartype; if (typeTree == null) return; String typeTreeToString = typeTree.toString(); - + if (!(eq(typeTreeToString, "val") || eq(typeTreeToString, "var"))) return; boolean isVal = typeMatches(val.class, localNode, typeTree); boolean isVar = typeMatches(var.class, localNode, typeTree); if (!(isVal || isVar)) return; - + if (isVal) handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); if (isVar) handleFlagUsage(localNode, ConfigurationKeys.VAR_FLAG_USAGE, "var"); - + JCTree parentRaw = localNode.directUp().get(); if (isVal && parentRaw instanceof JCForLoop) { localNode.addError("'val' is not allowed in old-style for loops"); return; } - + + if (parentRaw instanceof JCForLoop && ((JCForLoop) parentRaw).getInitializer().size() > 1) { + localNode.addError("'var' is not allowed in old-style for loops if there is more than 1 initializer"); + return; + } + JCExpression rhsOfEnhancedForLoop = null; if (local.init == null) { if (parentRaw instanceof JCEnhancedForLoop) { @@ -84,26 +89,26 @@ public class HandleVal extends JavacASTAdapter { if (efl.var == local) rhsOfEnhancedForLoop = efl.expr; } } - + final String annotation = typeTreeToString; if (rhsOfEnhancedForLoop == null && local.init == null) { localNode.addError("'" + annotation + "' on a local variable requires an initializer expression"); return; - } - + if (local.init instanceof JCNewArray && ((JCNewArray)local.init).elemtype == null) { localNode.addError("'" + annotation + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); return; } - + if (localNode.shouldDeleteLombokAnnotations()) { JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, val.class.getName()); + JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, lombok.experimental.var.class.getName()); JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, var.class.getName()); } - + if (isVal) local.mods.flags |= Flags.FINAL; - + if (!localNode.shouldDeleteLombokAnnotations()) { JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), typeTree, localNode.getContext()); local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation); @@ -126,7 +131,7 @@ public class HandleVal extends JavacASTAdapter { try { type = ((JCExpression) resolver.resolveMethodMember(localNode).get(local.init)).type; } catch (RuntimeException e) { - System.err.println("Exception while resolving: " + localNode); + System.err.println("Exception while resolving: " + localNode + "(" + localNode.getFileName() + ")"); throw e; } } else { @@ -137,7 +142,7 @@ public class HandleVal extends JavacASTAdapter { local.type = Symtab.instance(localNode.getContext()).unknownType; type = ((JCExpression) resolver.resolveMethodMember(localNode).get(local.init)).type; } catch (RuntimeException e) { - System.err.println("Exception while resolving: " + localNode); + System.err.println("Exception while resolving: " + localNode + "(" + localNode.getFileName() + ")"); throw e; } } diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java index 90f6a98d..d1af4168 100644 --- a/src/core/lombok/javac/handlers/HandleValue.java +++ b/src/core/lombok/javac/handlers/HandleValue.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2018 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 java.lang.annotation.Annotation; - import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; @@ -49,13 +47,16 @@ import com.sun.tools.javac.tree.JCTree.JCModifiers; @ProviderFor(JavacAnnotationHandler.class) @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(); + private HandleConstructor handleConstructor = new HandleConstructor(); + private HandleGetter handleGetter = new HandleGetter(); + private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode(); + private HandleToString handleToString = new HandleToString(); + @Override public void handle(AnnotationValues<Value> annotation, JCAnnotation ast, JavacNode annotationNode) { - @SuppressWarnings("deprecation") - Class<? extends Annotation> oldExperimentalValue = lombok.experimental.Value.class; - handleFlagUsage(annotationNode, ConfigurationKeys.VALUE_FLAG_USAGE, "@Value"); - deleteAnnotationIfNeccessary(annotationNode, Value.class, oldExperimentalValue); + deleteAnnotationIfNeccessary(annotationNode, Value.class, "lombok.experimental.Value"); JavacNode typeNode = annotationNode.up(); boolean notAClass = !isClass(typeNode); @@ -73,12 +74,10 @@ public class HandleValue extends JavacAnnotationHandler<Value> { typeNode.rebuild(); } } - new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true); - - // TODO move this to the end OR move it to the top in eclipse. - new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode); - new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); - new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); - new HandleToString().generateToStringForType(typeNode, annotationNode); + handleFieldDefaults.generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true); + handleConstructor.generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode); + handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode); + handleToString.generateToStringForType(typeNode, annotationNode); } } diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 56e666f7..65d09a9a 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2018 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 @@ -162,6 +162,10 @@ public class JavacHandlerUtil { return node; } + public static boolean hasAnnotation(String type, JavacNode node) { + return hasAnnotation(type, node, false); + } + public static boolean hasAnnotation(Class<? extends Annotation> type, JavacNode node) { return hasAnnotation(type, node, false); } @@ -191,6 +195,48 @@ public class JavacHandlerUtil { } } + private static boolean hasAnnotation(String type, JavacNode node, boolean delete) { + if (node == null) return false; + if (type == null) return false; + switch (node.getKind()) { + case ARGUMENT: + case FIELD: + case LOCAL: + case TYPE: + case METHOD: + for (JavacNode child : node.down()) { + if (annotationTypeMatches(type, child)) { + if (delete) deleteAnnotationIfNeccessary(child, type); + return true; + } + } + // intentional fallthrough + default: + return false; + } + } + + static JavacNode findAnnotation(Class<? extends Annotation> type, JavacNode node, boolean delete) { + if (node == null) return null; + if (type == null) return null; + switch (node.getKind()) { + case ARGUMENT: + case FIELD: + case LOCAL: + case TYPE: + case METHOD: + for (JavacNode child : node.down()) { + if (annotationTypeMatches(type, child)) { + if (delete) deleteAnnotationIfNeccessary(child, type); + return child; + } + } + // intentional fallthrough + default: + return null; + } + } + /** * Checks if the Annotation AST Node provided is likely to be an instance of the provided annotation type. * @@ -203,6 +249,17 @@ public class JavacHandlerUtil { } /** + * Checks if the Annotation AST Node provided is likely to be an instance of the provided annotation type. + * + * @param type An actual annotation type, such as {@code lombok.Getter.class}. + * @param node A Lombok AST node representing an annotation in source code. + */ + public static boolean annotationTypeMatches(String type, JavacNode node) { + if (node.getKind() != Kind.ANNOTATION) return false; + return typeMatches(type, node, ((JCAnnotation)node.get()).annotationType); + } + + /** * Checks if the given TypeReference node is likely to be a reference to the provided class. * * @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type. @@ -210,10 +267,21 @@ public class JavacHandlerUtil { * @param typeNode A type reference to check. */ public static boolean typeMatches(Class<?> type, JavacNode node, JCTree typeNode) { + return typeMatches(type.getName(), node, typeNode); + } + + /** + * Checks if the given TypeReference node is likely to be a reference to the provided class. + * + * @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type. + * @param node A Lombok AST node. Any node in the appropriate compilation unit will do (used to get access to import statements). + * @param typeNode A type reference to check. + */ + public static boolean typeMatches(String type, JavacNode node, JCTree typeNode) { String typeName = typeNode.toString(); TypeResolver resolver = new TypeResolver(node.getImportList()); - return resolver.typeMatches(node, type.getName(), typeName); + return resolver.typeMatches(node, type, typeName); } /** @@ -222,6 +290,7 @@ public class JavacHandlerUtil { * @return {@code true} if a field is marked deprecated, either by {@code @Deprecated} or in javadoc, otherwise {@code false} */ public static boolean isFieldDeprecated(JavacNode field) { + if (!(field.get() instanceof JCVariableDecl)) return false; JCVariableDecl fieldNode = (JCVariableDecl) field.get(); if ((fieldNode.mods.flags & Flags.DEPRECATED) != 0) { return true; @@ -324,8 +393,7 @@ public class JavacHandlerUtil { * then removes any import statement that imports this exact annotation (not star imports). * Only does this if the DeleteLombokAnnotations class is in the context. */ - @SuppressWarnings("unchecked") - public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType) { + public static void deleteAnnotationIfNeccessary(JavacNode annotation, String annotationType) { deleteAnnotationIfNeccessary0(annotation, annotationType); } @@ -334,12 +402,29 @@ public class JavacHandlerUtil { * then removes any import statement that imports this exact annotation (not star imports). * Only does this if the DeleteLombokAnnotations class is in the context. */ - @SuppressWarnings("unchecked") + public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType) { + deleteAnnotationIfNeccessary0(annotation, annotationType.getName()); + } + + /** + * Removes the annotation from javac's AST (it remains in lombok's AST), + * then removes any import statement that imports this exact annotation (not star imports). + * Only does this if the DeleteLombokAnnotations class is in the context. + */ public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType1, Class<? extends Annotation> annotationType2) { - deleteAnnotationIfNeccessary0(annotation, annotationType1, annotationType2); + deleteAnnotationIfNeccessary0(annotation, annotationType1.getName(), annotationType2.getName()); } - private static void deleteAnnotationIfNeccessary0(JavacNode annotation, Class<? extends Annotation>... annotationTypes) { + /** + * Removes the annotation from javac's AST (it remains in lombok's AST), + * then removes any import statement that imports this exact annotation (not star imports). + * Only does this if the DeleteLombokAnnotations class is in the context. + */ + public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType1, String annotationType2) { + deleteAnnotationIfNeccessary0(annotation, annotationType1.getName(), annotationType2); + } + + private static void deleteAnnotationIfNeccessary0(JavacNode annotation, String... annotationTypes) { if (inNetbeansEditor(annotation)) return; if (!annotation.shouldDeleteLombokAnnotations()) return; JavacNode parentNode = annotation.directUp(); @@ -368,8 +453,8 @@ public class JavacHandlerUtil { } parentNode.getAst().setChanged(); - for (Class<?> annotationType : annotationTypes) { - deleteImportFromCompilationUnit(annotation, annotationType.getName()); + for (String annotationType : annotationTypes) { + deleteImportFromCompilationUnit(annotation, annotationType); } } @@ -733,7 +818,7 @@ public class JavacHandlerUtil { // Check if the class has a @Getter annotation. - if (!hasGetterAnnotation && new HandleGetter().fieldQualifiesForGetterGeneration(field)) { + if (!hasGetterAnnotation && HandleGetter.fieldQualifiesForGetterGeneration(field)) { //Check if the class has @Getter or @Data annotation. JavacNode containingType = field.up(); @@ -1189,16 +1274,25 @@ public class JavacHandlerUtil { } /** - * Generates a new statement that checks if the given variable is null, and if so, throws a specified exception with the + * Generates a new statement that checks if the given variable is null, and if so, throws a configured exception with the * variable name as message. - * - * @param exName The name of the exception to throw; normally {@code java.lang.NullPointerException}. */ public static JCStatement generateNullCheck(JavacTreeMaker maker, JavacNode variable, JavacNode source) { + return generateNullCheck(maker, variable, (JCVariableDecl)variable.get(), source); + } + + /** + * Generates a new statement that checks if the given variable is null, and if so, throws a configured exception with the + * variable name as message. + * + * This is a special case method reserved for use when the provided declaration differs from the + * variable's declaration, i.e. in a constructor or setter where the local parameter is named the same but with the prefix + * stripped as a result of @Accessors.prefix. + */ + public static JCStatement generateNullCheck(JavacTreeMaker maker, JavacNode variable, JCVariableDecl varDecl, JavacNode source) { NullCheckExceptionType exceptionType = source.getAst().readConfiguration(ConfigurationKeys.NON_NULL_EXCEPTION_TYPE); if (exceptionType == null) exceptionType = NullCheckExceptionType.NULL_POINTER_EXCEPTION; - JCVariableDecl varDecl = (JCVariableDecl) variable.get(); if (isPrimitive(varDecl.vartype)) return null; Name fieldName = varDecl.name; JCExpression exType = genTypeRef(variable, exceptionType.getExceptionType()); @@ -1357,25 +1451,27 @@ public class JavacHandlerUtil { } public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, Name typeName, List<JCTypeParameter> params) { + if (params.isEmpty()) { + return maker.Ident(typeName); + } + return maker.TypeApply(maker.Ident(typeName), typeParameterNames(maker, params)); + } + + public static List<JCExpression> typeParameterNames(JavacTreeMaker maker, List<JCTypeParameter> params) { ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); - - if (!params.isEmpty()) { - for (JCTypeParameter param : params) { - typeArgs.append(maker.Ident(param.name)); - } - - return maker.TypeApply(maker.Ident(typeName), typeArgs.toList()); + for (JCTypeParameter param : params) { + typeArgs.append(maker.Ident(param.name)); } - - return maker.Ident(typeName); + return typeArgs.toList(); } public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(JavacNode typeNode, JavacNode errorNode) { List<String> disallowed = List.nil(); for (JavacNode child : typeNode.down()) { - for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) { + for (String annType : INVALID_ON_BUILDERS) { if (annotationTypeMatches(annType, child)) { - disallowed = disallowed.append(annType.getSimpleName()); + int lastIndex = annType.lastIndexOf('.'); + disallowed = disallowed.append(lastIndex == -1 ? annType : annType.substring(lastIndex + 1)); } } } diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 33501349..4279ec63 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 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,9 +38,12 @@ import lombok.javac.JavacTreeMaker; import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.code.BoundKind; +import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWildcard; @@ -141,6 +144,11 @@ public class JavacSingularsRecipes { public static abstract class JavacSingularizer { public abstract LombokImmutableList<String> getSupportedTypes(); + protected JCModifiers makeMods(JavacTreeMaker maker, JavacNode node, boolean deprecate) { + if (deprecate) return maker.Modifiers(Flags.PUBLIC, List.<JCAnnotation>of(maker.Annotation(genJavaLangTypeRef(node, "Deprecated"), List.<JCExpression>nil()))); + return maker.Modifiers(Flags.PUBLIC); + } + /** Checks if any of the to-be-generated nodes (fields, methods) already exist. If so, errors on these (singulars don't support manually writing some of it, and returns true). */ public boolean checkForAlreadyExistingNodesAndGenerateError(JavacNode builderType, SingularData data) { for (JavacNode child : builderType.down()) { @@ -186,7 +194,7 @@ public class JavacSingularsRecipes { } public abstract java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source); - public abstract void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain); + public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain); public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable); public boolean requiresCleaning() { diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index fb3d2c55..1798d52c 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 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 @@ -70,24 +70,24 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { JavacTreeMaker maker = builderType.getTreeMaker(); Symtab symbolTable = builderType.getSymbolTable(); JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateClearMethod(maker, returnType, returnStatement, data, builderType, source); + generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); } - private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { + JCModifiers mods = makeMods(maker, builderType, deprecate); List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); List<JCVariableDecl> params = List.nil(); @@ -102,7 +102,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { injectMethod(builderType, method); } - void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + void generateSingularMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); @@ -114,7 +114,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { names[i] = s.isEmpty() ? n : builderType.toName(s); } - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName()); @@ -141,11 +141,10 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { injectMethod(builderType, method); } - protected void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + protected void generatePluralMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); - - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); JCExpression thisDotFieldDotAddAll = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName() + "All"); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java index 7beb29cb..196ce45d 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 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 @@ -76,9 +76,9 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.generateMethods(data, builderType, source, fluent, chain); + guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, chain); return; } @@ -88,19 +88,19 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); JCStatement returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; - generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; - generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; - generateClearMethod(maker, returnType, returnStatement, data, builderType, source); + generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); } - private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { + JCModifiers mods = makeMods(maker, builderType, deprecate); List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); List<JCVariableDecl> params = List.nil(); @@ -119,11 +119,10 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize injectMethod(builderType, method); } - void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + void generateSingularMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); - - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source)); JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "add"); @@ -140,11 +139,10 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize injectMethod(builderType, method); } - void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + void generatePluralMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); - - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source)); JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "addAll"); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index 774b1200..fbcf776e 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 The Project Lombok Authors. + * Copyright (C) 2015-2017 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 @@ -101,9 +101,9 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.generateMethods(data, builderType, source, fluent, chain); + guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, chain); return; } @@ -112,19 +112,20 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateClearMethod(maker, returnType, returnStatement, data, builderType, source); + generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); } - private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { + JCModifiers mods = makeMods(maker, builderType, deprecate); + List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); List<JCVariableDecl> params = List.nil(); @@ -146,10 +147,11 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { injectMethod(builderType, method); } - private void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + private void generateSingularMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); + ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, true, source)); Name keyName = builderType.toName(data.getSingularName().toString() + "Key"); @@ -178,10 +180,10 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { injectMethod(builderType, method); } - private void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + private void generatePluralMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> jceBlank = List.nil(); - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, true, source)); long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext()); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java index 12922d3d..df521fd8 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java @@ -165,6 +165,9 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { JCExpression pluralnameDotPut = maker.Select(maker.Ident(data.getPluralName()), builderType.toName("put")); JCExpression arg1 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + "$key", "get"), List.<JCExpression>of(maker.Ident(ivar))); JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + "$value", "get"), List.<JCExpression>of(maker.Ident(ivar))); + // [jdk9] We add an unneccessary (V) cast here. Not doing so gives an error in javac (build 9-ea+156-jigsaw-nightly-h6072-20170212): + // error: method put in interface Map<K#2,V#2> cannot be applied to given types; + arg2 = maker.TypeCast(createTypeArgs(2, false, builderType, data.getTypeArgs(), source).get(1), arg2); JCStatement putStatement = maker.Exec(maker.Apply(jceBlank, pluralnameDotPut, List.of(arg1, arg2))); JCStatement forInit = maker.VarDef(maker.Modifiers(0), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0)); JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true, builderVariable)); |