diff options
Diffstat (limited to 'src/core/lombok/javac')
18 files changed, 373 insertions, 355 deletions
diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java index f6cd5571..86ff7646 100644 --- a/src/core/lombok/javac/JavacAST.java +++ b/src/core/lombok/javac/JavacAST.java @@ -79,6 +79,8 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { private final Log log; private final ErrorLog errorLogger; private final Context context; + private static final URI NOT_CALCULATED_MARKER = URI.create("https://projectlombok.org/not/calculated"); + private URI memoizedAbsoluteFileLocation = NOT_CALCULATED_MARKER; /** * Creates a new JavacAST of the provided Compilation Unit. @@ -102,7 +104,10 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { } @Override public URI getAbsoluteFileLocation() { - return getAbsoluteFileLocation((JCCompilationUnit) top().get()); + if (memoizedAbsoluteFileLocation == NOT_CALCULATED_MARKER) { + memoizedAbsoluteFileLocation = getAbsoluteFileLocation((JCCompilationUnit) top().get()); + } + return memoizedAbsoluteFileLocation; } public static URI getAbsoluteFileLocation(JCCompilationUnit cu) { @@ -258,7 +263,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { if (typeUse == null) return null; - if (typeUse.getClass().getSimpleName().equals("JCAnnotatedType")) { + if (typeUse.getClass().getName().equals("com.sun.tools.javac.tree.JCTree$JCAnnotatedType")) { initJcAnnotatedType(typeUse.getClass()); Collection<?> anns = Permit.permissiveReadField(Collection.class, JCANNOTATEDTYPE_ANNOTATIONS, typeUse); JCExpression underlying = Permit.permissiveReadField(JCExpression.class, JCANNOTATEDTYPE_UNDERLYINGTYPE, typeUse); @@ -381,7 +386,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { if (statement instanceof JCClassDecl) return buildType((JCClassDecl) statement); if (statement instanceof JCVariableDecl) return buildLocalVar((JCVariableDecl) statement, Kind.LOCAL); if (statement instanceof JCTry) return buildTry((JCTry) statement); - if (statement.getClass().getSimpleName().equals("JCLambda")) return buildLambda(statement); + if (statement.getClass().getName().equals("com.sun.tools.javac.tree.JCTree$JCLambda")) return buildLambda(statement); if (setAndGetAsHandled(statement)) return null; return drill(statement); diff --git a/src/core/lombok/javac/JavacNode.java b/src/core/lombok/javac/JavacNode.java index 19bbeae3..3de3f38b 100644 --- a/src/core/lombok/javac/JavacNode.java +++ b/src/core/lombok/javac/JavacNode.java @@ -27,10 +27,6 @@ import java.util.List; import javax.lang.model.element.Element; import javax.tools.Diagnostic; -import lombok.core.AnnotationValues; -import lombok.core.AST.Kind; -import lombok.javac.handlers.JavacHandlerUtil; - import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.model.JavacTypes; @@ -43,8 +39,12 @@ import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.Name; + +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.javac.handlers.JavacHandlerUtil; /** * Javac specific version of the LombokNode class. @@ -345,6 +345,29 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre return mods != null && (Flags.ENUM & mods.flags) != 0; } + @Override public boolean isPrimitive() { + if (node instanceof JCVariableDecl && !isEnumMember()) { + return Javac.isPrimitive(((JCVariableDecl) node).vartype); + } + if (node instanceof JCMethodDecl) { + return Javac.isPrimitive(((JCMethodDecl) node).restype); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override public String fieldOrMethodBaseType() { + if (node instanceof JCVariableDecl && !isEnumMember()) { + return (((JCVariableDecl) node).vartype).toString(); + } + if (node instanceof JCMethodDecl) { + return (((JCMethodDecl) node).restype).toString(); + } + return null; + } + @Override public boolean isTransient() { if (getKind() != Kind.FIELD) return false; JCModifiers mods = getModifiers(); diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java index 9a0077a7..6b2f4637 100644 --- a/src/core/lombok/javac/JavacResolution.java +++ b/src/core/lombok/javac/JavacResolution.java @@ -28,7 +28,10 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Iterator; import java.util.Map; +import java.util.NoSuchElementException; import javax.lang.model.type.TypeKind; import javax.tools.JavaFileObject; @@ -40,6 +43,7 @@ import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.ArrayType; import com.sun.tools.javac.code.Type.CapturedType; import com.sun.tools.javac.code.Type.ClassType; +import com.sun.tools.javac.code.Type.TypeVar; import com.sun.tools.javac.code.Type.WildcardType; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.comp.Attr; @@ -59,6 +63,7 @@ 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; +import com.sun.tools.javac.util.Name; import lombok.Lombok; import lombok.core.debug.AssertionLogger; @@ -314,6 +319,41 @@ public class JavacResolution { return result; } + private static Iterable<? extends Type> concat(final Type t, final Collection<? extends Type> ts) { + if (t == null) return ts; + + return new Iterable<Type>() { + @Override public Iterator<Type> iterator() { + return new Iterator<Type>() { + private boolean first = true; + private Iterator<? extends Type> wrap = ts == null ? null : ts.iterator(); + @Override public boolean hasNext() { + if (first) return true; + if (wrap == null) return false; + return wrap.hasNext(); + } + + @Override public Type next() { + if (first) { + first = false; + return t; + } + if (wrap == null) throw new NoSuchElementException(); + return wrap.next(); + } + + @Override public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + private static int compare(Name a, Name b) { + return a.compareTo(b); + } + private static JCExpression typeToJCTree0(Type type, JavacAST ast, boolean allowCompound, boolean allowVoid, boolean allowCapture) throws TypeNotConvertibleException { // NB: There's such a thing as maker.Type(type), but this doesn't work very well; it screws up anonymous classes, captures, and adds an extra prefix dot for some reason too. // -- so we write our own take on that here. @@ -335,12 +375,30 @@ public class JavacResolution { if (symbol.name.length() == 0) { // Anonymous inner class if (type instanceof ClassType) { - List<Type> ifaces = ((ClassType) type).interfaces_field; + Type winner = null; + int winLevel = 0; // 100 = array, 50 = class, 20 = typevar, 15 = wildcard, 10 = interface, 1 = Object. Type supertype = ((ClassType) type).supertype_field; - if (isObject(supertype) && ifaces != null && ifaces.length() > 0) { - return typeToJCTree(ifaces.get(0), ast, allowCompound, allowVoid, allowCapture); + List<Type> ifaces = ((ClassType) type).interfaces_field; + for (Type t : concat(supertype, ifaces)) { + int level = 0; + if (t instanceof ArrayType) level = 100; + else if (t instanceof TypeVar) level = 20; + else if (t instanceof WildcardType) level = 15; + else if (t.isInterface()) level = 10; + else if (isObject(t)) level = 1; + else if (t instanceof ClassType) level = 50; + else level = 5; + + if (winLevel > level) continue; + if (winLevel < level) { + winner = t; + winLevel = level; + continue; + } + if (compare(winner.tsym.getQualifiedName(), t.tsym.getQualifiedName()) < 0) winner = t; } - if (supertype != null) return typeToJCTree(supertype, ast, allowCompound, allowVoid, allowCapture); + if (winner == null) return createJavaLangObject(ast); + return typeToJCTree(winner, ast, allowCompound, allowVoid, allowCapture); } throw new TypeNotConvertibleException("Anonymous inner class"); } diff --git a/src/core/lombok/javac/JavacTransformer.java b/src/core/lombok/javac/JavacTransformer.java index 625fb283..a2e00680 100644 --- a/src/core/lombok/javac/JavacTransformer.java +++ b/src/core/lombok/javac/JavacTransformer.java @@ -21,7 +21,7 @@ */ package lombok.javac; -import java.util.ArrayList; +import java.util.List; import java.util.SortedSet; import javax.annotation.processing.Messager; @@ -34,7 +34,6 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.List; import lombok.ConfigurationKeys; import lombok.core.CleanupRegistry; @@ -57,31 +56,15 @@ public class JavacTransformer { return handlers.getPrioritiesRequiringResolutionReset(); } - public void transform(long priority, Context context, java.util.List<JCCompilationUnit> compilationUnitsRaw, CleanupRegistry cleanup) { - List<JCCompilationUnit> compilationUnits; - if (compilationUnitsRaw instanceof List<?>) { - compilationUnits = (List<JCCompilationUnit>) compilationUnitsRaw; - } else { - compilationUnits = List.nil(); - for (int i = compilationUnitsRaw.size() -1; i >= 0; i--) { - compilationUnits = compilationUnits.prepend(compilationUnitsRaw.get(i)); - } - } - - java.util.List<JavacAST> asts = new ArrayList<JavacAST>(); - + public void transform(long priority, Context context, List<JCCompilationUnit> compilationUnits, CleanupRegistry cleanup) { for (JCCompilationUnit unit : compilationUnits) { if (!Boolean.TRUE.equals(LombokConfiguration.read(ConfigurationKeys.LOMBOK_DISABLE, JavacAST.getAbsoluteFileLocation(unit)))) { - asts.add(new JavacAST(messager, context, unit, cleanup)); + JavacAST ast = new JavacAST(messager, context, unit, cleanup); + ast.traverse(new AnnotationVisitor(priority)); + handlers.callASTVisitors(ast, priority); + if (ast.isChanged()) LombokOptions.markChanged(context, (JCCompilationUnit) ast.top().get()); } } - - for (JavacAST ast : asts) { - ast.traverse(new AnnotationVisitor(priority)); - handlers.callASTVisitors(ast, priority); - } - - for (JavacAST ast : asts) if (ast.isChanged()) LombokOptions.markChanged(context, (JCCompilationUnit) ast.top().get()); } private class AnnotationVisitor extends JavacASTAdapter { diff --git a/src/core/lombok/javac/apt/Javac9BaseFileObjectWrapper.java b/src/core/lombok/javac/apt/Javac9BaseFileObjectWrapper.java deleted file mode 100644 index f71be366..00000000 --- a/src/core/lombok/javac/apt/Javac9BaseFileObjectWrapper.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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 - * 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.apt; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; -import java.net.URI; -import java.nio.file.Path; - -import javax.lang.model.element.Modifier; -import javax.lang.model.element.NestingKind; - -import com.sun.tools.javac.file.BaseFileManager; - -class Javac9BaseFileObjectWrapper extends com.sun.tools.javac.file.PathFileObject { - private final LombokFileObject delegate; - - public Javac9BaseFileObjectWrapper(BaseFileManager fileManager, Path path, LombokFileObject delegate) { - super(fileManager, path); - this.delegate = delegate; - } - - @Override public boolean isNameCompatible(String simpleName, Kind kind) { - return delegate.isNameCompatible(simpleName, kind); - } - - @Override public URI toUri() { - return delegate.toUri(); - } - - @SuppressWarnings("all") - @Override public String getName() { - return delegate.getName(); - } - - @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - return delegate.getCharContent(ignoreEncodingErrors); - } - - @Override public InputStream openInputStream() throws IOException { - return delegate.openInputStream(); - } - - @Override public Reader openReader(boolean ignoreEncodingErrors) throws IOException { - return delegate.openReader(ignoreEncodingErrors); - } - - @Override public Writer openWriter() throws IOException { - return delegate.openWriter(); - } - - @Override public OutputStream openOutputStream() throws IOException { - return delegate.openOutputStream(); - } - - @Override public long getLastModified() { - return delegate.getLastModified(); - } - - @Override public boolean delete() { - return delegate.delete(); - } - - @Override public Kind getKind() { - return delegate.getKind(); - } - - @Override public NestingKind getNestingKind() { - return delegate.getNestingKind(); - } - - @Override public Modifier getAccessLevel() { - return delegate.getAccessLevel(); - } - - @Override public boolean equals(Object obj) { - if (!(obj instanceof Javac9BaseFileObjectWrapper)) return false; - return delegate.equals(((Javac9BaseFileObjectWrapper)obj).delegate); - } - - @Override public int hashCode() { - return delegate.hashCode(); - } - - @Override public String toString() { - return delegate.toString(); - } -}
\ 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 f6643db3..880ef1fd 100644 --- a/src/core/lombok/javac/apt/LombokFileObjects.java +++ b/src/core/lombok/javac/apt/LombokFileObjects.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2019 The Project Lombok Authors. + * Copyright (C) 2010-2020 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,23 +22,15 @@ package lombok.javac.apt; -import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; 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 lombok.permit.Permit; @@ -116,22 +108,20 @@ final class LombokFileObjects { if (jfmClassName.equals("com.sun.tools.javac.file.JavacFileManager")) { try { Class<?> superType = Class.forName("com.sun.tools.javac.file.BaseFileManager"); - if (superType.isInstance(jfm)) { - return new Java9Compiler(jfm); - } + if (superType.isInstance(jfm)) return java9Compiler(jfm); } catch (Throwable e) {} return Compiler.JAVAC7; } if (KNOWN_JAVA9_FILE_MANAGERS.contains(jfmClassName)) { try { - return new Java9Compiler(jfm); + return java9Compiler(jfm); } catch (Throwable e) {} } try { if (Class.forName("com.sun.tools.javac.file.PathFileObject") == null) throw new NullPointerException(); - return new Java9Compiler(jfm); + return java9Compiler(jfm); } catch (Throwable e) {} try { if (Class.forName("com.sun.tools.javac.file.BaseFileObject") == null) throw new NullPointerException(); @@ -156,122 +146,24 @@ final class LombokFileObjects { return compiler.wrap(new InterceptingJavaFileObject(delegate, fileName, diagnostics, compiler.getDecoderMethod())); } - static class Java9Compiler implements Compiler { - private final BaseFileManager fileManager; - - public Java9Compiler(JavaFileManager jfm) { - fileManager = asBaseFileManager(jfm); - } - - @Override public JavaFileObject wrap(LombokFileObject fileObject) { - Path p; try { - p = toPath(fileObject); - } catch (Exception e) { - p = null; - } - - // J9BFOW extends javac's internal file base impl of javax.tools.JavaFileObject. - // J9JFOW just straight implements it. Probably J9JFOW is fine, but we decided to extend java's internal impl possibly for a reason. - // Some exotic build environments don't _have_ file objects and crash with FileNotFoundEx, so if that happens, let's try the alternative. - if (p != null) return new Javac9BaseFileObjectWrapper(fileManager, p, fileObject); - return new Javac9JavaFileObjectWrapper(fileObject); - } - - @Override public Method getDecoderMethod() { + private static Constructor<?> j9CompilerConstructor = null; + private static Compiler java9Compiler(JavaFileManager jfm) { + try { + if (j9CompilerConstructor == null) j9CompilerConstructor = Class.forName("lombok.javac.apt.Java9Compiler").getConstructor(JavaFileManager.class); + return (Compiler) j9CompilerConstructor.newInstance(jfm); + } catch (ClassNotFoundException e) { return null; - } - - private static Path toPath(LombokFileObject fileObject) { - URI uri = fileObject.toUri(); - if (uri.getScheme() == null) { - uri = URI.create("file:///" + uri); - } - try { - return Paths.get(uri); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Problems in URI '" + uri + "' (" + fileObject.toUri() + ")", e); - } - } - - 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(); - } + } catch (NoSuchMethodException e) { + return null; + } catch (InvocationTargetException e) { + Throwable t = e.getCause(); + if (t instanceof RuntimeException) throw (RuntimeException) t; + if (t instanceof Error) throw (Error) t; + throw new RuntimeException(t); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); } } } diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java index c32e09d5..852e5de6 100644 --- a/src/core/lombok/javac/apt/LombokProcessor.java +++ b/src/core/lombok/javac/apt/LombokProcessor.java @@ -24,6 +24,7 @@ package lombok.javac.apt; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; @@ -42,7 +43,6 @@ import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; -import javax.lang.model.element.QualifiedNameable; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic.Kind; import javax.tools.JavaFileManager; @@ -371,7 +371,7 @@ public class LombokProcessor extends AbstractProcessor { private String getModuleNameFor(Element element) { while (element != null) { - if (element.getKind().name().equals("MODULE")) return ModuleNameOracle.getModuleName(element); + if (element.getKind().name().equals("MODULE")) return getModuleName(element); Element n = element.getEnclosingElement(); if (n == element) return null; element = n; @@ -379,12 +379,24 @@ public class LombokProcessor extends AbstractProcessor { return null; } - // QualifiedNameable is a java7 thing, so to remain compatible with java6, shove this into an inner class to avoid the ClassNotFoundError. - private static class ModuleNameOracle { - static String getModuleName(Element element) { - if (!(element instanceof QualifiedNameable)) return null; - String name = ((QualifiedNameable) element).getQualifiedName().toString().trim(); + private static Class<?> qualifiedNamableClass = null; + private static Method qualifiedNamableQualifiedNameMethod = null; + // QualifiedNameable isn't in java 6, so to remain compatible with java6, use reflection. + private static String getModuleName(Element element) { + try { + if (qualifiedNamableClass == null) qualifiedNamableClass = Class.forName("javax.lang.model.element.QualifiedNamable"); + if (!qualifiedNamableClass.isInstance(element)) return null; + if (qualifiedNamableQualifiedNameMethod == null) qualifiedNamableQualifiedNameMethod = qualifiedNamableClass.getMethod("getQualifiedName"); + String name = qualifiedNamableQualifiedNameMethod.invoke(element).toString().trim(); return name.isEmpty() ? null : name; + } catch (ClassNotFoundException e) { + return null; + } catch (NoSuchMethodException e) { + return null; + } catch (InvocationTargetException e) { + return null; + } catch (IllegalAccessException e) { + return null; } } diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 91a74d62..7e87ce11 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -27,6 +27,8 @@ import static lombok.javac.JavacTreeMaker.TypeTag.typeTag; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.lang.model.element.Modifier; @@ -38,6 +40,7 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; import com.sun.tools.javac.tree.JCTree.JCBlock; 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; @@ -432,7 +435,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { java.util.List<Included<JavacNode, ToString.Include>> fieldNodes = new ArrayList<Included<JavacNode, ToString.Include>>(); for (BuilderFieldData bfd : builderFields) { for (JavacNode f : bfd.createdFields) { - fieldNodes.add(new Included<JavacNode, ToString.Include>(f, null, true)); + fieldNodes.add(new Included<JavacNode, ToString.Include>(f, null, true, false)); } } @@ -845,11 +848,30 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { newMethod.params = List.of(recv, newMethod.params.get(0)); } recursiveSetGeneratedBy(newMethod, source.get(), builderType.getContext()); - copyJavadoc(originalFieldNode, newMethod, CopyJavadoc.SETTER, true); + if (source.up().getKind() == Kind.METHOD) { + copyJavadocFromParam(originalFieldNode.up(), newMethod, paramName.toString()); + } else { + copyJavadoc(originalFieldNode, newMethod, CopyJavadoc.SETTER, true); + } injectMethod(builderType, newMethod); } + private void copyJavadocFromParam(JavacNode from, JCMethodDecl to, String param) { + try { + JCCompilationUnit cu = ((JCCompilationUnit) from.top().get()); + String methodComment = Javac.getDocComment(cu, from.get()); + if (methodComment == null) return; + + Pattern pattern = Pattern.compile("@param " + param + " (\\S|\\s)+?(?=^ ?@)", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(methodComment); + if (matcher.find()) { + String newJavadoc = addReturnsThisIfNeeded(matcher.group()); + Javac.setDocComment(cu, to, newJavadoc); + } + } catch (Exception ignore) {} + } + public JavacNode makeBuilderClass(boolean isStatic, JavacNode source, JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast, AccessLevel access) { JavacTreeMaker maker = tdParent.getTreeMaker(); int modifiers = toJavacModifier(access); diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index e0abb53b..2a683767 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -68,6 +68,7 @@ import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult; public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) public static class HandleNoArgsConstructor extends JavacAnnotationHandler<NoArgsConstructor> { + private static final String NAME = NoArgsConstructor.class.getSimpleName(); private HandleConstructor handleConstructor = new HandleConstructor(); @Override public void handle(AnnotationValues<NoArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { @@ -76,7 +77,7 @@ public class HandleConstructor { deleteAnnotationIfNeccessary(annotationNode, NoArgsConstructor.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); - if (!checkLegality(typeNode, annotationNode, NoArgsConstructor.class.getSimpleName())) return; + if (!checkLegality(typeNode, annotationNode, NAME)) return; List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode); NoArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); @@ -89,6 +90,7 @@ public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) public static class HandleRequiredArgsConstructor extends JavacAnnotationHandler<RequiredArgsConstructor> { + private static final String NAME = RequiredArgsConstructor.class.getSimpleName(); private HandleConstructor handleConstructor = new HandleConstructor(); @Override public void handle(AnnotationValues<RequiredArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { @@ -97,7 +99,7 @@ public class HandleConstructor { deleteAnnotationIfNeccessary(annotationNode, RequiredArgsConstructor.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); - if (!checkLegality(typeNode, annotationNode, RequiredArgsConstructor.class.getSimpleName())) return; + if (!checkLegality(typeNode, annotationNode, NAME)) return; List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor", annotationNode); RequiredArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); @@ -138,6 +140,7 @@ public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) public static class HandleAllArgsConstructor extends JavacAnnotationHandler<AllArgsConstructor> { + private static final String NAME = AllArgsConstructor.class.getSimpleName(); private HandleConstructor handleConstructor = new HandleConstructor(); @Override public void handle(AnnotationValues<AllArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { @@ -146,7 +149,7 @@ public class HandleConstructor { deleteAnnotationIfNeccessary(annotationNode, AllArgsConstructor.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); - if (!checkLegality(typeNode, annotationNode, AllArgsConstructor.class.getSimpleName())) return; + if (!checkLegality(typeNode, annotationNode, NAME)) return; List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor", annotationNode); AllArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); diff --git a/src/core/lombok/javac/handlers/HandleDelegate.java b/src/core/lombok/javac/handlers/HandleDelegate.java index 367b2cff..d6e76ab1 100644 --- a/src/core/lombok/javac/handlers/HandleDelegate.java +++ b/src/core/lombok/javac/handlers/HandleDelegate.java @@ -48,6 +48,7 @@ import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Attribute.Compound; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.ClassType; @@ -175,14 +176,14 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> { List<MethodSig> signaturesToExclude = new ArrayList<MethodSig>(); Set<String> banList = new HashSet<String>(); banList.addAll(METHODS_IN_OBJECT); - /* To exclude all methods in the class itself, try this: - for (Symbol member : ((JCClassDecl)typeNode.get()).sym.getEnclosedElements()) { - if (member instanceof MethodSymbol) { - MethodSymbol method = (MethodSymbol) member; - banList.add(printSig((ExecutableType) method.asType(), method.name, annotationNode.getTypesUtil())); + + // Add already implemented methods to ban list + JavacNode typeNode = upToTypeNode(annotationNode); + for (Symbol m : ((JCClassDecl)typeNode.get()).sym.getEnclosedElements()) { + if (m instanceof MethodSymbol) { + banList.add(printSig((ExecutableType) m.asType(), m.name, annotationNode.getTypesUtil())); } } - */ try { for (Type t : toExclude) { @@ -389,10 +390,11 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> { boolean isDeprecated = (member.flags() & DEPRECATED) != 0; signatures.add(new MethodSig(member.name, methodType, isDeprecated, exElem)); } - - if (ct.supertype_field instanceof ClassType) addMethodBindings(signatures, (ClassType) ct.supertype_field, types, banList); - if (ct.interfaces_field != null) for (Type iface : ct.interfaces_field) { - if (iface instanceof ClassType) addMethodBindings(signatures, (ClassType) iface, types, banList); + + for (Type type : types.directSupertypes(ct)) { + if (type instanceof ClassType) { + addMethodBindings(signatures, (ClassType) type, types, banList); + } } } diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 985873c1..110941d6 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -232,9 +232,17 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { List<JCStatement> statements; JCTree toClearOfMarkers = null; + int[] methodArgPos = null; boolean addSuppressWarningsUnchecked = false; if (lazy && !inNetbeansEditor(field)) { toClearOfMarkers = fieldNode.init; + if (toClearOfMarkers instanceof JCMethodInvocation) { + List<JCExpression> args = ((JCMethodInvocation) toClearOfMarkers).args; + methodArgPos = new int[args.length()]; + for (int i = 0; i < methodArgPos.length; i++) { + methodArgPos[i] = args.get(i).pos; + } + } statements = createLazyGetterBody(treeMaker, field, source); addSuppressWarningsUnchecked = LombokOptionsFactory.getDelombokOptions(field.getContext()).getFormatPreferences().generateSuppressWarnings(); } else { @@ -262,10 +270,20 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source, field.getContext()); if (toClearOfMarkers != null) recursiveSetGeneratedBy(toClearOfMarkers, null, null); + if (methodArgPos != null) { + for (int i = 0; i < methodArgPos.length; i++) { + ((JCMethodInvocation) toClearOfMarkers).args.get(i).pos = methodArgPos[i]; + } + } decl.mods.annotations = decl.mods.annotations.appendList(delegates); if (addSuppressWarningsUnchecked) { - addAnnotation(decl.mods, field, source.pos, source, field.getContext(), "java.lang.SuppressWarnings", treeMaker.NewArray(null, List.<JCExpression>nil(), List.<JCExpression>of(treeMaker.Literal("all"), treeMaker.Literal("unchecked")))); + ListBuffer<JCExpression> suppressions = new ListBuffer<JCExpression>(); + if (!Boolean.FALSE.equals(field.getAst().readConfiguration(ConfigurationKeys.ADD_SUPPRESSWARNINGS_ANNOTATIONS))) { + suppressions.add(treeMaker.Literal("all")); + } + suppressions.add(treeMaker.Literal("unchecked")); + addAnnotation(decl.mods, field, source.pos, source, field.getContext(), "java.lang.SuppressWarnings", treeMaker.NewArray(null, List.<JCExpression>nil(), suppressions.toList())); } copyJavadoc(field, decl, CopyJavadoc.GETTER); diff --git a/src/core/lombok/javac/handlers/HandleJacksonized.java b/src/core/lombok/javac/handlers/HandleJacksonized.java index aff0bf63..0aa02d1b 100644 --- a/src/core/lombok/javac/handlers/HandleJacksonized.java +++ b/src/core/lombok/javac/handlers/HandleJacksonized.java @@ -149,7 +149,6 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> { // @SuperBuilder? Make it package-private! if (superBuilderAnnotationNode != null) builderClass.mods.flags = builderClass.mods.flags & ~Flags.PRIVATE; - } private String getBuilderClassName(JCAnnotation ast, JavacNode annotationNode, JavacNode annotatedNode, JCClassDecl td, AnnotationValues<Builder> builderAnnotation, JavacTreeMaker maker) { diff --git a/src/core/lombok/javac/handlers/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java index 522f8576..3173b3ba 100644 --- a/src/core/lombok/javac/handlers/HandleLog.java +++ b/src/core/lombok/javac/handlers/HandleLog.java @@ -56,7 +56,7 @@ public class HandleLog { throw new UnsupportedOperationException(); } - public static void processAnnotation(LoggingFramework framework, AnnotationValues<?> annotation, JavacNode annotationNode, String loggerTopic) { + public static void processAnnotation(LoggingFramework framework, AnnotationValues<?> annotation, JavacNode annotationNode) { deleteAnnotationIfNeccessary(annotationNode, framework.getAnnotationClass()); JavacNode typeNode = annotationNode.up(); @@ -76,14 +76,17 @@ public class HandleLog { return; } - if (loggerTopic != null && loggerTopic.trim().isEmpty()) loggerTopic = null; + Object valueGuess = annotation.getValueGuess("topic"); + JCExpression loggerTopic = (JCExpression) annotation.getActualExpression("topic"); + + if (valueGuess instanceof String && ((String) valueGuess).trim().isEmpty()) loggerTopic = null; if (framework.getDeclaration().getParametersWithTopic() == null && loggerTopic != null) { annotationNode.addError(framework.getAnnotationAsString() + " does not allow a topic."); loggerTopic = null; } if (framework.getDeclaration().getParametersWithoutTopic() == null && loggerTopic == null) { annotationNode.addError(framework.getAnnotationAsString() + " requires a topic."); - loggerTopic = ""; + loggerTopic = typeNode.getTreeMaker().Literal(""); } JCFieldAccess loggingType = selfType(typeNode); @@ -101,7 +104,7 @@ public class HandleLog { return maker.Select(maker.Ident(name), typeNode.toName("class")); } - private static boolean createField(LoggingFramework framework, JavacNode typeNode, JCFieldAccess loggingType, JCTree source, String logFieldName, boolean useStatic, String loggerTopic) { + private static boolean createField(LoggingFramework framework, JavacNode typeNode, JCFieldAccess loggingType, JCTree source, String logFieldName, boolean useStatic, JCExpression loggerTopic) { JavacTreeMaker maker = typeNode.getTreeMaker(); LogDeclaration logDeclaration = framework.getDeclaration(); @@ -121,7 +124,7 @@ public class HandleLog { return true; } - private static JCExpression[] createFactoryParameters(JavacNode typeNode, JCFieldAccess loggingType, java.util.List<LogFactoryParameter> parameters, String loggerTopic) { + private static JCExpression[] createFactoryParameters(JavacNode typeNode, JCFieldAccess loggingType, java.util.List<LogFactoryParameter> parameters, JCExpression loggerTopic) { JCExpression[] expressions = new JCExpression[parameters.size()]; JavacTreeMaker maker = typeNode.getTreeMaker(); @@ -136,7 +139,7 @@ public class HandleLog { expressions[i] = maker.Apply(List.<JCExpression>nil(), method, List.<JCExpression>nil()); break; case TOPIC: - expressions[i] = maker.Literal(loggerTopic); + expressions[i] = (JCExpression) loggerTopic.clone(); break; case NULL: expressions[i] = maker.Literal(CTC_BOT, null); @@ -156,7 +159,7 @@ public class HandleLog { public static class HandleCommonsLog extends JavacAnnotationHandler<lombok.extern.apachecommons.CommonsLog> { @Override public void handle(AnnotationValues<lombok.extern.apachecommons.CommonsLog> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_COMMONS_FLAG_USAGE, "@apachecommons.CommonsLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); - processAnnotation(LoggingFramework.COMMONS, annotation, annotationNode, annotation.getInstance().topic()); + processAnnotation(LoggingFramework.COMMONS, annotation, annotationNode); } } @@ -167,7 +170,7 @@ public class HandleLog { public static class HandleJulLog extends JavacAnnotationHandler<lombok.extern.java.Log> { @Override public void handle(AnnotationValues<lombok.extern.java.Log> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_JUL_FLAG_USAGE, "@java.Log", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); - processAnnotation(LoggingFramework.JUL, annotation, annotationNode, annotation.getInstance().topic()); + processAnnotation(LoggingFramework.JUL, annotation, annotationNode); } } @@ -178,7 +181,7 @@ public class HandleLog { public static class HandleLog4jLog extends JavacAnnotationHandler<lombok.extern.log4j.Log4j> { @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_LOG4J_FLAG_USAGE, "@Log4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); - processAnnotation(LoggingFramework.LOG4J, annotation, annotationNode, annotation.getInstance().topic()); + processAnnotation(LoggingFramework.LOG4J, annotation, annotationNode); } } @@ -189,7 +192,7 @@ public class HandleLog { public static class HandleLog4j2Log extends JavacAnnotationHandler<lombok.extern.log4j.Log4j2> { @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j2> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_LOG4J2_FLAG_USAGE, "@Log4j2", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); - processAnnotation(LoggingFramework.LOG4J2, annotation, annotationNode, annotation.getInstance().topic()); + processAnnotation(LoggingFramework.LOG4J2, annotation, annotationNode); } } @@ -200,7 +203,7 @@ public class HandleLog { public static class HandleSlf4jLog extends JavacAnnotationHandler<lombok.extern.slf4j.Slf4j> { @Override public void handle(AnnotationValues<lombok.extern.slf4j.Slf4j> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_SLF4J_FLAG_USAGE, "@Slf4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); - processAnnotation(LoggingFramework.SLF4J, annotation, annotationNode, annotation.getInstance().topic()); + processAnnotation(LoggingFramework.SLF4J, annotation, annotationNode); } } @@ -211,7 +214,7 @@ public class HandleLog { public static class HandleXSlf4jLog extends JavacAnnotationHandler<lombok.extern.slf4j.XSlf4j> { @Override public void handle(AnnotationValues<lombok.extern.slf4j.XSlf4j> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_XSLF4J_FLAG_USAGE, "@XSlf4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); - processAnnotation(LoggingFramework.XSLF4J, annotation, annotationNode, annotation.getInstance().topic()); + processAnnotation(LoggingFramework.XSLF4J, annotation, annotationNode); } } @@ -222,7 +225,7 @@ public class HandleLog { public static class HandleJBossLog extends JavacAnnotationHandler<lombok.extern.jbosslog.JBossLog> { @Override public void handle(AnnotationValues<lombok.extern.jbosslog.JBossLog> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_JBOSSLOG_FLAG_USAGE, "@JBossLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); - processAnnotation(LoggingFramework.JBOSSLOG, annotation, annotationNode, annotation.getInstance().topic()); + processAnnotation(LoggingFramework.JBOSSLOG, annotation, annotationNode); } } @@ -233,7 +236,7 @@ public class HandleLog { public static class HandleFloggerLog extends JavacAnnotationHandler<lombok.extern.flogger.Flogger> { @Override public void handle(AnnotationValues<lombok.extern.flogger.Flogger> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.LOG_FLOGGER_FLAG_USAGE, "@Flogger", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); - processAnnotation(LoggingFramework.FLOGGER, annotation, annotationNode, ""); + processAnnotation(LoggingFramework.FLOGGER, annotation, annotationNode); } } @@ -250,7 +253,7 @@ public class HandleLog { return; } LoggingFramework framework = new LoggingFramework(lombok.CustomLog.class, logDeclaration); - processAnnotation(framework, annotation, annotationNode, annotation.getInstance().topic()); + processAnnotation(framework, annotation, annotationNode); } } } diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 2dc3247d..f6bf9e1f 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -304,7 +304,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { java.util.List<Included<JavacNode, ToString.Include>> fieldNodes = new ArrayList<Included<JavacNode, ToString.Include>>(); for (BuilderFieldData bfd : builderFields) { for (JavacNode f : bfd.createdFields) { - fieldNodes.add(new Included<JavacNode, ToString.Include>(f, null, true)); + fieldNodes.add(new Included<JavacNode, ToString.Include>(f, null, true, false)); } } @@ -354,8 +354,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { } // Generate a constructor in the annotated class that takes a builder as argument. - generateBuilderBasedConstructor(cfv, tdParent, typeParams, builderFields, annotationNode, builderClassName, - superclassBuilderClassExpression != null); + if (!constructorExists(tdParent, builderClassName)) { + generateBuilderBasedConstructor(cfv, tdParent, typeParams, builderFields, annotationNode, builderClassName, + superclassBuilderClassExpression != null); + } if (isAbstract) { // Only non-abstract classes get the builder() and toBuilder() methods. @@ -375,14 +377,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { if (toBuilder) { switch (methodExists(TO_BUILDER_METHOD_NAME, tdParent, 0)) { case EXISTS_BY_USER: - annotationNode.addWarning("Not generating toBuilder() as it already exists."); - return; + break; case NOT_EXISTS: JCMethodDecl md = generateToBuilderMethod(cfv, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); if (md != null) { recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); injectMethod(tdParent, md); } + break; default: // Should not happen. } @@ -1070,4 +1072,33 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { } return typeParameter; } + + /** + * Checks if there is a manual constructor in the given type with a single parameter (builder). + */ + private boolean constructorExists(JavacNode type, String builderClassName) { + if (type != null && type.get() instanceof JCClassDecl) { + for (JCTree def : ((JCClassDecl)type.get()).defs) { + if (def instanceof JCMethodDecl) { + JCMethodDecl md = (JCMethodDecl) def; + String name = md.name.toString(); + boolean matches = name.equals("<init>"); + if (isTolerate(type, md)) + continue; + if (matches && md.params != null && md.params.length() == 1) { + // Cannot use typeMatches() here, because the parameter could be fully-qualified, partially-qualified, or not qualified. + // A string-compare of the last part should work. If it's a false-positive, users could still @Tolerate it. + String typeName = md.params.get(0).getType().toString(); + int lastIndexOfDot = typeName.lastIndexOf('.'); + if (lastIndexOfDot >= 0) { + typeName = typeName.substring(lastIndexOfDot+1); + } + if ((builderClassName+"<?, ?>").equals(typeName)) + return true; + } + } + } + } + return false; + } } diff --git a/src/core/lombok/javac/handlers/HandleUtilityClass.java b/src/core/lombok/javac/handlers/HandleUtilityClass.java index 95de4c47..5d651689 100644 --- a/src/core/lombok/javac/handlers/HandleUtilityClass.java +++ b/src/core/lombok/javac/handlers/HandleUtilityClass.java @@ -110,7 +110,7 @@ public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> { if (typeNode.up().getKind() == Kind.COMPILATION_UNIT) markStatic = false; if (markStatic && typeNode.up().getKind() == Kind.TYPE) { JCClassDecl typeDecl = (JCClassDecl) typeNode.up().get(); - if ((typeDecl.mods.flags & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0) markStatic = false; + if ((typeDecl.mods.flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0) markStatic = false; } if (markStatic) classDecl.mods.flags |= Flags.STATIC; diff --git a/src/core/lombok/javac/handlers/HandleWith.java b/src/core/lombok/javac/handlers/HandleWith.java index 6977b10e..4e35a574 100644 --- a/src/core/lombok/javac/handlers/HandleWith.java +++ b/src/core/lombok/javac/handlers/HandleWith.java @@ -234,7 +234,8 @@ public class HandleWith extends JavacAnnotationHandler<With> { long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, field.getContext()); List<JCAnnotation> annsOnParam = copyAnnotations(onParam).appendList(copyableAnnotations); - JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, annsOnParam), fieldDecl.name, fieldDecl.vartype, null); + JCExpression pType = cloneType(maker, fieldDecl.vartype, source.get(), source.getContext()); + JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, annsOnParam), fieldDecl.name, pType, null); if (!makeAbstract) { ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index a3e876c4..efc4bf61 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -40,6 +40,7 @@ import java.util.regex.Pattern; import javax.lang.model.element.Element; +import com.sun.tools.javac.code.Attribute; import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Scope; @@ -1127,7 +1128,25 @@ public class JavacHandlerUtil { } } } - + + static class JCAnnotationReflect { + private static Field ATTRIBUTE; + + static { + try { + ATTRIBUTE = Permit.getField(JCAnnotation.class, "attribute"); + } catch (Exception ignore) {} + } + + static Attribute.Compound getAttribute(JCAnnotation jcAnnotation) { + try { + return (Attribute.Compound) ATTRIBUTE.get(jcAnnotation); + } catch (Exception e) { + return null; + } + } + } + // jdk9 support, types have changed, names stay the same static class ClassSymbolMembersField { private static final Field membersField; @@ -1178,8 +1197,10 @@ public class JavacHandlerUtil { * Also takes care of updating the JavacAST. */ public static void injectMethod(JavacNode typeNode, JCMethodDecl method, List<Type> paramTypes, Type returnType) { + Context context = typeNode.getContext(); + Symtab symtab = Symtab.instance(context); JCClassDecl type = (JCClassDecl) typeNode.get(); - + if (method.getName().contentEquals("<init>")) { //Scan for default constructor, and remove it. int idx = 0; @@ -1196,20 +1217,53 @@ public class JavacHandlerUtil { idx++; } } - + addSuppressWarningsAll(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext()); addGenerated(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext()); type.defs = type.defs.append(method); - - fixMethodMirror(typeNode.getContext(), typeNode.getElement(), method.getModifiers().flags, method.getName(), paramTypes, returnType); - + + List<Symbol.VarSymbol> params = null; + if (method.getParameters() != null && !method.getParameters().isEmpty()) { + ListBuffer<Symbol.VarSymbol> newParams = new ListBuffer<Symbol.VarSymbol>(); + for (int i = 0; i < method.getParameters().size(); i++) { + JCTree.JCVariableDecl param = method.getParameters().get(i); + if (param.sym == null) { + Type paramType = paramTypes == null ? param.getType().type : paramTypes.get(i); + VarSymbol varSymbol = new VarSymbol(param.mods.flags, param.name, paramType, symtab.noSymbol); + List<JCAnnotation> annotations = param.getModifiers().getAnnotations(); + if (annotations != null && !annotations.isEmpty()) { + ListBuffer<Attribute.Compound> newAnnotations = new ListBuffer<Attribute.Compound>(); + for (JCAnnotation jcAnnotation : annotations) { + Attribute.Compound attribute = JCAnnotationReflect.getAttribute(jcAnnotation); + if (attribute != null) { + newAnnotations.append(attribute); + } + } + if (annotations.length() == newAnnotations.length()) { + varSymbol.appendAttributes(newAnnotations.toList()); + } + } + newParams.append(varSymbol); + } else { + newParams.append(param.sym); + } + } + params = newParams.toList(); + if (params.length() != method.getParameters().length()) params = null; + } + + fixMethodMirror(typeNode.getContext(), typeNode.getElement(), method.getModifiers().flags, method.getName(), paramTypes, params, returnType); + typeNode.add(method, Kind.METHOD); } - - private static void fixMethodMirror(Context context, Element typeMirror, long access, Name methodName, List<Type> paramTypes, Type returnType) { + + private static void fixMethodMirror(Context context, Element typeMirror, long access, Name methodName, List<Type> paramTypes, List<Symbol.VarSymbol> params, Type returnType) { if (typeMirror == null || paramTypes == null || returnType == null) return; ClassSymbol cs = (ClassSymbol) typeMirror; MethodSymbol methodSymbol = new MethodSymbol(access, methodName, new MethodType(paramTypes, returnType, List.<Type>nil(), Symtab.instance(context).methodClass), cs); + if (params != null && !params.isEmpty()) { + methodSymbol.params = params; + } ClassSymbolMembersField.enter(cs, methodSymbol); } @@ -1265,15 +1319,17 @@ public class JavacHandlerUtil { public static void addSuppressWarningsAll(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context) { if (!LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateSuppressWarnings()) return; - boolean addJLSuppress = true; + boolean addJLSuppress = !Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_SUPPRESSWARNINGS_ANNOTATIONS)); - for (JCAnnotation ann : mods.annotations) { - JCTree type = ann.getAnnotationType(); - Name n = null; - if (type instanceof JCIdent) n = ((JCIdent) type).name; - else if (type instanceof JCFieldAccess) n = ((JCFieldAccess) type).name; - if (n != null && n.contentEquals("SuppressWarnings")) { - addJLSuppress = false; + if (addJLSuppress) { + for (JCAnnotation ann : mods.annotations) { + JCTree type = ann.getAnnotationType(); + Name n = null; + if (type instanceof JCIdent) n = ((JCIdent) type).name; + else if (type instanceof JCFieldAccess) n = ((JCFieldAccess) type).name; + if (n != null && n.contentEquals("SuppressWarnings")) { + addJLSuppress = false; + } } } if (addJLSuppress) addAnnotation(mods, node, pos, source, context, "java.lang.SuppressWarnings", node.getTreeMaker().Literal("all")); @@ -1520,6 +1576,20 @@ public class JavacHandlerUtil { * Searches the given field node for annotations that are specifically intentioned to be copied to the setter. */ public static List<JCAnnotation> findCopyableToSetterAnnotations(JavacNode node) { + return findAnnotationsInList(node, COPY_TO_SETTER_ANNOTATIONS); + } + + /** + * Searches the given field node for annotations that are specifically intentioned to be copied to the builder's singular method. + */ + public static List<JCAnnotation> findCopyableToBuilderSingularSetterAnnotations(JavacNode node) { + return findAnnotationsInList(node, COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS); + } + + /** + * Searches the given field node for annotations that are in the given list, and returns those. + */ + private static List<JCAnnotation> findAnnotationsInList(JavacNode node, java.util.List<String> annotationsToFind) { JCAnnotation anno = null; String annoName = null; for (JavacNode child : node.down()) { @@ -1537,7 +1607,7 @@ public class JavacHandlerUtil { if (annoName == null) return List.nil(); if (!annoName.isEmpty()) { - for (String bn : COPY_TO_SETTER_ANNOTATIONS) if (typeMatches(bn, node, anno.annotationType)) return List.of(anno); + for (String bn : annotationsToFind) if (typeMatches(bn, node, anno.annotationType)) return List.of(anno); } ListBuffer<JCAnnotation> result = new ListBuffer<JCAnnotation>(); @@ -1545,7 +1615,7 @@ public class JavacHandlerUtil { if (child.getKind() == Kind.ANNOTATION) { JCAnnotation annotation = (JCAnnotation) child.get(); boolean match = false; - if (!match) for (String bn : COPY_TO_SETTER_ANNOTATIONS) if (typeMatches(bn, node, annotation.annotationType)) { + if (!match) for (String bn : annotationsToFind) if (typeMatches(bn, node, annotation.annotationType)) { result.append(annotation); break; } @@ -1935,6 +2005,7 @@ public class JavacHandlerUtil { } private static final Pattern SECTION_FINDER = Pattern.compile("^\\s*\\**\\s*[-*][-*]+\\s*([GS]ETTER|WITH(?:ER)?)\\s*[-*][-*]+\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + private static final Pattern LINE_BREAK_FINDER = Pattern.compile("(\\r?\\n)?"); public static String stripLinesWithTagFromJavadoc(String javadoc, String regexpFragment) { Pattern p = Pattern.compile("^\\s*\\**\\s*" + regexpFragment + "\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); @@ -1943,27 +2014,29 @@ public class JavacHandlerUtil { } public static String stripSectionsFromJavadoc(String javadoc) { - Matcher m = SECTION_FINDER.matcher(javadoc); - if (!m.find()) return javadoc; + Matcher sectionMatcher = SECTION_FINDER.matcher(javadoc); + if (!sectionMatcher.find()) return javadoc; - return javadoc.substring(0, m.start()); + return javadoc.substring(0, sectionMatcher.start()); } public static String getJavadocSection(String javadoc, String sectionNameSpec) { String[] sectionNames = sectionNameSpec.split("\\|"); - Matcher m = SECTION_FINDER.matcher(javadoc); + Matcher sectionMatcher = SECTION_FINDER.matcher(javadoc); + Matcher lineBreakMatcher = LINE_BREAK_FINDER.matcher(javadoc); int sectionStart = -1; int sectionEnd = -1; - while (m.find()) { + while (sectionMatcher.find()) { boolean found = false; - for (String sectionName : sectionNames) if (m.group(1).equalsIgnoreCase(sectionName)) { + for (String sectionName : sectionNames) if (sectionMatcher.group(1).equalsIgnoreCase(sectionName)) { found = true; break; } if (found) { - sectionStart = m.end() + 1; + lineBreakMatcher.find(sectionMatcher.end()); + sectionStart = lineBreakMatcher.end(); } else if (sectionStart != -1) { - sectionEnd = m.start(); + sectionEnd = sectionMatcher.start(); } } diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index ba052b5a..7cd52c8c 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -182,11 +182,12 @@ public class JavacSingularsRecipes { return this; } - protected JCModifiers makeMods(JavacTreeMaker maker, CheckerFrameworkVersion cfv, JavacNode node, boolean deprecate, AccessLevel access) { + protected JCModifiers makeMods(JavacTreeMaker maker, CheckerFrameworkVersion cfv, JavacNode node, boolean deprecate, AccessLevel access, List<JCAnnotation> methodAnnotations) { JCAnnotation deprecateAnn = deprecate ? maker.Annotation(genJavaLangTypeRef(node, "Deprecated"), List.<JCExpression>nil()) : null; JCAnnotation rrAnn = cfv.generateReturnsReceiver() ? maker.Annotation(genTypeRef(node, CheckerFrameworkVersion.NAME__RETURNS_RECEIVER), List.<JCExpression>nil()) : null; List<JCAnnotation> annsOnMethod = (deprecateAnn != null && rrAnn != null) ? List.of(deprecateAnn, rrAnn) : deprecateAnn != null ? List.of(deprecateAnn) : rrAnn != null ? List.of(rrAnn) : List.<JCAnnotation>nil(); + annsOnMethod = mergeAnnotations(annsOnMethod,methodAnnotations); return maker.Modifiers(toJavacModifier(access), annsOnMethod); } @@ -271,10 +272,10 @@ public class JavacSingularsRecipes { generateClearMethod(cfv, deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, access); } - private void finishAndInjectMethod(CheckerFrameworkVersion cfv, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean deprecate, ListBuffer<JCStatement> statements, Name methodName, List<JCVariableDecl> jcVariableDecls, AccessLevel access, Boolean ignoreNullCollections) { + private void finishAndInjectMethod(CheckerFrameworkVersion cfv, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean deprecate, ListBuffer<JCStatement> statements, Name methodName, List<JCVariableDecl> jcVariableDecls, List<JCAnnotation> methodAnnotations, AccessLevel access, Boolean ignoreNullCollections) { if (returnStatement != null) statements.append(returnStatement); JCBlock body = maker.Block(0, statements.toList()); - JCModifiers mods = makeMods(maker, cfv, builderType, deprecate, access); + JCModifiers mods = makeMods(maker, cfv, builderType, deprecate, access, methodAnnotations); List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); @@ -298,7 +299,7 @@ public class JavacSingularsRecipes { statements.add(clearStatement); Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString())); - finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, methodName, List.<JCVariableDecl>nil(), access, null); + finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, methodName, List.<JCVariableDecl>nil(), List.<JCAnnotation>nil(), access, null); } protected abstract JCStatement generateClearStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType); @@ -312,7 +313,8 @@ public class JavacSingularsRecipes { if (!setterPrefix.isEmpty()) name = builderType.toName(HandlerUtil.buildAccessorName(setterPrefix, name.toString())); statements.prepend(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); - finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, access, null); + List<JCAnnotation> methodAnnotations = copyAnnotations(findCopyableToBuilderSingularSetterAnnotations(data.annotation.up())); + finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, methodAnnotations, access, null); } protected JCVariableDecl generateSingularMethodParameter(int typeIndex, JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source, Name name) { @@ -357,8 +359,10 @@ public class JavacSingularsRecipes { } else { statements.prepend(JavacHandlerUtil.generateNullCheck(maker, null, data.getPluralName(), builderType, "%s cannot be null")); } + + List<JCAnnotation> methodAnnotations = copyAnnotations(findCopyableToSetterAnnotations(data.annotation.up())); - finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, List.of(param), access, ignoreNullCollections); + finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, List.of(param), methodAnnotations, access, ignoreNullCollections); } protected ListBuffer<JCStatement> generatePluralMethodStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { |