From 7e94041dbc177476020969b00d3411020418f903 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 25 Jul 2018 23:19:00 +0200 Subject: Added key ‘dangerousconfig.lombok.disable’. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/core/src/lombok/AbstractRunTests.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/core/src/lombok/AbstractRunTests.java b/test/core/src/lombok/AbstractRunTests.java index 3d672bc4..f93fbe27 100644 --- a/test/core/src/lombok/AbstractRunTests.java +++ b/test/core/src/lombok/AbstractRunTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 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 @@ -30,6 +30,7 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.StringWriter; +import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -40,7 +41,6 @@ import java.util.Map; import org.junit.Assert; import lombok.DirectoryRunner.FileTester; -import lombok.core.AST; import lombok.core.LombokConfiguration; import lombok.core.LombokImmutableList; import lombok.core.configuration.ConfigurationKeysLoader; @@ -84,7 +84,7 @@ public abstract class AbstractRunTests { StringWriter writer = new StringWriter(); LombokConfiguration.overrideConfigurationResolverFactory(new ConfigurationResolverFactory() { - @Override public ConfigurationResolver createResolver(AST ast) { + @Override public ConfigurationResolver createResolver(URI sourceLocation) { return sourceDirectives_.getConfiguration(); } }); -- cgit From 3d432c38d0cfeb36b8e7402eca56faaafcf7bac7 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Sun, 29 Jul 2018 16:37:21 +0200 Subject: code impl of FieldNameConstants rewrite + test updates for it --- src/core/lombok/ConfigurationKeys.java | 11 +- .../eclipse/handlers/HandleFieldNameConstants.java | 144 +++++++++++++-------- .../lombok/experimental/FieldNameConstants.java | 28 +++- .../javac/handlers/HandleFieldNameConstants.java | 129 ++++++++++-------- .../after-delombok/FieldNameConstantsBasic.java | 8 +- .../FieldNameConstantsConfigKeys.java | 7 +- .../after-delombok/FieldNameConstantsEnum.java | 10 ++ .../after-delombok/FieldNameConstantsWeird.java | 6 - .../after-ecj/FieldNameConstantsBasic.java | 18 ++- .../after-ecj/FieldNameConstantsConfigKeys.java | 13 +- .../resource/after-ecj/FieldNameConstantsEnum.java | 23 ++++ .../after-ecj/FieldNameConstantsWeird.java | 13 -- .../resource/before/FieldNameConstantsBasic.java | 4 +- .../before/FieldNameConstantsConfigKeys.java | 3 +- .../resource/before/FieldNameConstantsEnum.java | 13 ++ .../resource/before/FieldNameConstantsWeird.java | 12 -- .../FieldNameConstantsWeird.java.messages | 1 - .../FieldNameConstantsWeird.java.messages | 1 - 18 files changed, 270 insertions(+), 174 deletions(-) create mode 100644 test/transform/resource/after-delombok/FieldNameConstantsEnum.java delete mode 100644 test/transform/resource/after-delombok/FieldNameConstantsWeird.java create mode 100644 test/transform/resource/after-ecj/FieldNameConstantsEnum.java delete mode 100644 test/transform/resource/after-ecj/FieldNameConstantsWeird.java create mode 100644 test/transform/resource/before/FieldNameConstantsEnum.java delete mode 100644 test/transform/resource/before/FieldNameConstantsWeird.java delete mode 100644 test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages delete mode 100644 test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages (limited to 'test') diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 1a28c0fa..184ded27 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -529,18 +529,11 @@ public class ConfigurationKeys { public static final ConfigurationKey FIELD_NAME_CONSTANTS_FLAG_USAGE = new ConfigurationKey("lombok.fieldNameConstants.flagUsage", "Emit a warning or error if @FieldNameConstants is used.") {}; /** - * lombok configuration: {@code lombok.fieldNameConstants.prefix} = <String: aJavaIdentifierPrefix> (Default: {@code PREFIX_}). + * lombok configuration: {@code lombok.fieldNameConstants.innerTypeName} = <String: AValidJavaTypeName> (Default: {@code Fields}). * * The names of the constants generated by {@code @FieldNameConstants} will be prefixed with this value. */ - public static final ConfigurationKey FIELD_NAME_CONSTANTS_PREFIX = new ConfigurationKey("lombok.fieldNameConstants.prefix", "names of constants generated by @FieldNameConstants will be prefixed with this value. (default: 'PREFIX_').") {}; - - /** - * lombok configuration: {@code lombok.fieldNameConstants.suffix} = <String: aJavaIdentifierPrefix> (Default: nothing). - * - * The names of the constants generated by {@code @FieldNameConstants} will be suffixed with this value. - */ - public static final ConfigurationKey FIELD_NAME_CONSTANTS_SUFFIX = new ConfigurationKey("lombok.fieldNameConstants.suffix", "names of constants generated by @FieldNameConstants will be suffixed with this value. (default: nothing).") {}; + public static final ConfigurationKey FIELD_NAME_CONSTANTS_INNER_TYPE_NAME = new ConfigurationKey("lombok.fieldNameConstants.innerTypeName", "The default name of the inner type generated by @FieldNameConstants. (default: 'Fields').") {}; // ----- Wither ----- diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java index c3a28f7f..15650490 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java @@ -24,32 +24,37 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; -import java.lang.reflect.Modifier; -import java.util.Collection; +import java.util.ArrayList; +import java.util.List; import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.handlers.HandlerUtil; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.FieldNameConstants; import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.Clinit; +import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.mangosdk.spi.ProviderFor; @ProviderFor(EclipseAnnotationHandler.class) public class HandleFieldNameConstants extends EclipseAnnotationHandler { - public void generateFieldNameConstantsForType(EclipseNode typeNode, EclipseNode errorNode, AccessLevel level, String prefix, String suffix) { + public void generateFieldNameConstantsForType(EclipseNode typeNode, EclipseNode errorNode, AccessLevel level, boolean asEnum, String innerTypeName, boolean onlyExplicit) { TypeDeclaration typeDecl = null; if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); @@ -57,22 +62,29 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler qualified = new ArrayList(); + for (EclipseNode field : typeNode.down()) { - if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level, prefix, suffix); + if (fieldQualifiesForFieldNameConstantsGeneration(field, onlyExplicit)) qualified.add(field); + } + + if (qualified.isEmpty()) { + errorNode.addWarning("No fields qualify for @FieldNameConstants, therefore this annotation does nothing"); + } else { + createInnerTypeFieldNameConstants(typeNode, errorNode.get(), level, qualified, asEnum, innerTypeName); } } - private void generateFieldNameConstantsForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, String prefix, String suffix) { - if (hasAnnotation(FieldNameConstants.class, fieldNode)) return; - createFieldNameConstantsForField(level, prefix, suffix, fieldNode, fieldNode, pos, false); - } - - private boolean fieldQualifiesForFieldNameConstantsGeneration(EclipseNode field) { + private boolean fieldQualifiesForFieldNameConstantsGeneration(EclipseNode field, boolean onlyExplicit) { if (field.getKind() != Kind.FIELD) return false; + if (hasAnnotation(FieldNameConstants.Exclude.class, field)) return false; + if (hasAnnotation(FieldNameConstants.Include.class, field)) return true; + if (onlyExplicit) return false; + FieldDeclaration fieldDecl = (FieldDeclaration) field.get(); return filterField(fieldDecl); } @@ -81,55 +93,75 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler fieldNodes, EclipseNode errorNode, ASTNode source, boolean whineIfExists) { - for (EclipseNode fieldNode : fieldNodes) createFieldNameConstantsForField(level, prefix, suffix, fieldNode, errorNode, source, whineIfExists); - } - - private void createFieldNameConstantsForField(AccessLevel level, String prefix, String suffix, EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists) { - if (fieldNode.getKind() != Kind.FIELD) { - errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field"); + FieldNameConstants annotationInstance = annotation.getInstance(); + AccessLevel level = annotationInstance.level(); + boolean asEnum = annotationInstance.asEnum(); + boolean usingLombokv1_18_2 = annotation.isExplicit("prefix") || annotation.isExplicit("suffix") || node.getKind() == Kind.FIELD; + + if (usingLombokv1_18_2) { + annotationNode.addError("@FieldNameConstants has been redesigned in lombok v1.18.4; please upgrade your project dependency on lombok. See https://projectlombok.org/features/experimental/FieldNameConstants for more information."); return; } - FieldDeclaration field = (FieldDeclaration) fieldNode.get(); - String fieldName = new String(field.name); - String constantName = prefix + HandlerUtil.camelCaseToConstant(fieldName) + suffix; - if (constantName.equals(fieldName)) { - fieldNode.addWarning("Not generating constant for this field: The name of the constant would be equal to the name of this field."); + if (level == AccessLevel.NONE) { + annotationNode.addWarning("AccessLevel.NONE is not compatible with @FieldNameConstants. If you don't want the inner type, simply remove FieldNameConstants."); return; } - int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long) pS << 32 | pE; - FieldDeclaration fieldConstant = new FieldDeclaration(constantName.toCharArray(), pS,pE); - fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; - fieldConstant.modifiers = toEclipseModifier(level) | Modifier.STATIC | Modifier.FINAL; - fieldConstant.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p,p,p}); - fieldConstant.initialization = new StringLiteral(field.name, pS, pE, 0); - injectField(fieldNode.up(), fieldConstant); + String innerTypeName = annotationInstance.innerTypeName(); + if (innerTypeName.isEmpty()) innerTypeName = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_INNER_TYPE_NAME); + if (innerTypeName == null || innerTypeName.isEmpty()) innerTypeName = "Fields"; + + generateFieldNameConstantsForType(node, annotationNode, level, asEnum, innerTypeName, annotationInstance.onlyExplicitlyIncluded()); + } + + private void createInnerTypeFieldNameConstants(EclipseNode typeNode, ASTNode source, AccessLevel level, List fields, boolean asEnum, String innerTypeName) { + if (fields.isEmpty()) return; + + TypeDeclaration parent = (TypeDeclaration) typeNode.get(); + TypeDeclaration innerType = new TypeDeclaration(parent.compilationResult); + innerType.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + innerType.modifiers = toEclipseModifier(level) | (asEnum ? ClassFileConstants.AccEnum : ClassFileConstants.AccStatic | ClassFileConstants.AccFinal); + char[] name = innerTypeName.toCharArray(); + innerType.name = name; + innerType.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + EclipseNode innerNode = injectType(typeNode, innerType); + + ConstructorDeclaration constructor = new ConstructorDeclaration(parent.compilationResult); + constructor.selector = name; + constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart; + constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; + constructor.modifiers = ClassFileConstants.AccPrivate; + ExplicitConstructorCall superCall = new ExplicitConstructorCall(0); + superCall.sourceStart = source.sourceStart; + superCall.sourceEnd = source.sourceEnd; + superCall.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + constructor.constructorCall = superCall; + if (!asEnum) constructor.statements = new Statement[0]; + + injectMethod(innerNode, constructor); + + if (asEnum) injectMethod(innerNode, new Clinit(parent.compilationResult)); + + for (EclipseNode fieldNode : fields) { + FieldDeclaration field = (FieldDeclaration) fieldNode.get(); + char[] fName = field.name; + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long) pS << 32 | pE; + FieldDeclaration fieldConstant = new FieldDeclaration(fName, pS, pE); + fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + fieldConstant.modifiers = asEnum ? 0 : ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal; + fieldConstant.type = asEnum ? null : new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p, p, p}); + if (asEnum) { + AllocationExpression ac = new AllocationExpression(); + ac.enumConstant = fieldConstant; + ac.sourceStart = source.sourceStart; + ac.sourceEnd = source.sourceEnd; + fieldConstant.initialization = ac; + } else { + fieldConstant.initialization = new StringLiteral(field.name, pS, pE, 0); + } + injectField(innerNode, fieldConstant); + } } } diff --git a/src/core/lombok/experimental/FieldNameConstants.java b/src/core/lombok/experimental/FieldNameConstants.java index 31c2970c..d6886890 100644 --- a/src/core/lombok/experimental/FieldNameConstants.java +++ b/src/core/lombok/experimental/FieldNameConstants.java @@ -29,12 +29,32 @@ import java.lang.annotation.Target; import lombok.AccessLevel; /** - * Generates String constants containing the field name for each field. + * Generates an inner type, containing String constants containing the field name for each field. Alternatively, generates an inner enum with enum values matching each field name. */ -@Target({ElementType.TYPE, ElementType.FIELD}) +@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface FieldNameConstants { lombok.AccessLevel level() default AccessLevel.PUBLIC; - String prefix() default " CONFIG DEFAULT "; - String suffix() default " CONFIG DEFAULT "; + boolean asEnum() default false; + String innerTypeName() default ""; + + /** + * Only include fields and methods explicitly marked with {@code @FieldNameConstants.Include}. + * Normally, all (non-static) fields are included by default. + */ + boolean onlyExplicitlyIncluded() default false; + + /** + * If present, do not include this field in the generated fieldnames inner type. + */ + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.SOURCE) + public @interface Exclude {} + + /** + * If present, include this field in the generated fieldnames inner type (default). + */ + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.SOURCE) + public @interface Include {} } diff --git a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java index 8ff136fc..5b120948 100644 --- a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java @@ -24,13 +24,11 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; import static lombok.javac.handlers.JavacHandlerUtil.*; -import java.lang.reflect.Modifier; -import java.util.Collection; +import java.util.ArrayList; import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.core.AST.Kind; -import lombok.core.handlers.HandlerUtil; import lombok.core.AnnotationValues; import lombok.experimental.FieldNameConstants; import lombok.javac.JavacAnnotationHandler; @@ -40,16 +38,23 @@ import lombok.javac.JavacTreeMaker; import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCModifiers; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Name; @ProviderFor(JavacAnnotationHandler.class) public class HandleFieldNameConstants extends JavacAnnotationHandler { - public void generateFieldNameConstantsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, String prefix, String suffix) { + public void generateFieldNameConstantsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean asEnum, String innerTypeName, boolean onlyExplicit) { JCClassDecl typeDecl = null; if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); @@ -57,22 +62,31 @@ public class HandleFieldNameConstants extends JavacAnnotationHandler qualified = new ArrayList(); + for (JavacNode field : typeNode.down()) { - if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level, prefix, suffix); + if (fieldQualifiesForFieldNameConstantsGeneration(field, onlyExplicit)) qualified.add(field); + } + + if (qualified.isEmpty()) { + errorNode.addWarning("No fields qualify for @FieldNameConstants, therefore this annotation does nothing"); + } else { + createInnerTypeFieldNameConstants(typeNode, errorNode.get(), level, qualified, asEnum, innerTypeName); } } - private void generateFieldNameConstantsForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, String prefix, String suffix) { - if (hasAnnotation(FieldNameConstants.class, fieldNode)) return; - createFieldNameConstantsForField(level, prefix, suffix, fieldNode, fieldNode, false); - } - - private boolean fieldQualifiesForFieldNameConstantsGeneration(JavacNode field) { + private boolean fieldQualifiesForFieldNameConstantsGeneration(JavacNode field, boolean onlyExplicit) { if (field.getKind() != Kind.FIELD) return false; + boolean exclAnn = JavacHandlerUtil.hasAnnotationAndDeleteIfNeccessary(FieldNameConstants.Exclude.class, field); + boolean inclAnn = JavacHandlerUtil.hasAnnotationAndDeleteIfNeccessary(FieldNameConstants.Include.class, field); + if (exclAnn) return false; + if (inclAnn) return true; + if (onlyExplicit) return false; + JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); if (fieldDecl.name.toString().startsWith("$")) return false; if ((fieldDecl.mods.flags & Flags.STATIC) != 0) return false; @@ -82,56 +96,61 @@ public class HandleFieldNameConstants extends JavacAnnotationHandler annotation, JCAnnotation ast, JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_NAME_CONSTANTS_FLAG_USAGE, "@FieldNameConstants"); - Collection fields = annotationNode.upFromAnnotationToFields(); deleteAnnotationIfNeccessary(annotationNode, FieldNameConstants.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode node = annotationNode.up(); - FieldNameConstants annotatationInstance = annotation.getInstance(); - AccessLevel level = annotatationInstance.level(); - String prefix = annotatationInstance.prefix(); - String suffix = annotatationInstance.suffix(); - if (prefix.equals(" CONFIG DEFAULT ")) prefix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_PREFIX); - if (suffix.equals(" CONFIG DEFAULT ")) suffix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_SUFFIX); - if (prefix == null) prefix = "FIELD_"; - if (suffix == null) suffix = ""; - if (node == null) return; - switch (node.getKind()) { - case FIELD: - if (level != AccessLevel.NONE) createFieldNameConstantsForFields(level, prefix, suffix, fields, annotationNode, annotationNode, true); - break; - case TYPE: - if (level == AccessLevel.NONE) { - annotationNode.addWarning("type-level '@FieldNameConstants' does not work with AccessLevel.NONE."); - return; - } - generateFieldNameConstantsForType(node, annotationNode, level, prefix, suffix); - break; - } - } - - private void createFieldNameConstantsForFields(AccessLevel level, String prefix, String suffix, Collection fieldNodes, JavacNode annotationNode, JavacNode errorNode, boolean whineIfExists) { - for (JavacNode fieldNode : fieldNodes) createFieldNameConstantsForField(level, prefix, suffix, fieldNode, errorNode, whineIfExists); - } - - private void createFieldNameConstantsForField(AccessLevel level, String prefix, String suffix, JavacNode fieldNode, JavacNode source, boolean whineIfExists) { - if (fieldNode.getKind() != Kind.FIELD) { - source.addError("@FieldNameConstants is only supported on a class, an enum, or a field"); + FieldNameConstants annotationInstance = annotation.getInstance(); + AccessLevel level = annotationInstance.level(); + boolean asEnum = annotationInstance.asEnum(); + boolean usingLombokv1_18_2 = annotation.isExplicit("prefix") || annotation.isExplicit("suffix") || node.getKind() == Kind.FIELD; + + if (usingLombokv1_18_2) { + annotationNode.addError("@FieldNameConstants has been redesigned in lombok v1.18.4; please upgrade your project dependency on lombok. See https://projectlombok.org/features/experimental/FieldNameConstants for more information."); return; } - JCVariableDecl field = (JCVariableDecl) fieldNode.get(); - String fieldName = field.name.toString(); - String constantName = prefix + HandlerUtil.camelCaseToConstant(fieldName) + suffix; - if (constantName.equals(fieldName)) { - fieldNode.addWarning("Not generating constant for this field: The name of the constant would be equal to the name of this field."); + + if (level == AccessLevel.NONE) { + annotationNode.addWarning("AccessLevel.NONE is not compatible with @FieldNameConstants. If you don't want the inner type, simply remove @FieldNameConstants."); return; } - JavacTreeMaker treeMaker = fieldNode.getTreeMaker(); - JCModifiers modifiers = treeMaker.Modifiers(toJavacModifier(level) | Modifier.STATIC | Modifier.FINAL); - JCExpression returnType = chainDots(fieldNode, "java", "lang", "String"); - JCExpression init = treeMaker.Literal(fieldNode.getName()); - JCVariableDecl fieldConstant = treeMaker.VarDef(modifiers, fieldNode.toName(constantName), returnType, init); - injectField(fieldNode.up(), fieldConstant); + String innerTypeName = annotationInstance.innerTypeName(); + if (innerTypeName.isEmpty()) innerTypeName = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_INNER_TYPE_NAME); + if (innerTypeName == null || innerTypeName.isEmpty()) innerTypeName = "Fields"; + + generateFieldNameConstantsForType(node, annotationNode, level, asEnum, innerTypeName, annotationInstance.onlyExplicitlyIncluded()); + } + + private void createInnerTypeFieldNameConstants(JavacNode typeNode, DiagnosticPosition pos, AccessLevel level, java.util.List fields, boolean asEnum, String innerTypeName) { + if (fields.isEmpty()) return; + + JavacTreeMaker maker = typeNode.getTreeMaker(); + JCModifiers mods = maker.Modifiers(toJavacModifier(level) | (asEnum ? Flags.ENUM : Flags.STATIC | Flags.FINAL)); + + Name fieldsName = typeNode.toName(innerTypeName); + JCClassDecl innerType = maker.ClassDef(mods, fieldsName, List.nil(), null, List.nil(), List.nil()); + JavacNode innerNode = injectType(typeNode, innerType); + + JCModifiers genConstrMods = maker.Modifiers(Flags.GENERATEDCONSTR | (asEnum ? 0L : Flags.PRIVATE)); + JCBlock genConstrBody = maker.Block(0L, List.of(maker.Exec(maker.Apply(List.nil(), maker.Ident(typeNode.toName("super")), List.nil())))); + JCMethodDecl genConstr = maker.MethodDef(genConstrMods, typeNode.toName(""), null, List.nil(), List.nil(), List.nil(), genConstrBody, null); + + injectMethod(innerNode, genConstr); + + for (JavacNode field : fields) { + JCModifiers enumValueMods = maker.Modifiers(Flags.PUBLIC | Flags.STATIC | Flags.FINAL | (asEnum ? Flags.ENUM : 0L)); + JCExpression returnType; + JCExpression init; + if (asEnum) { + returnType = maker.Ident(fieldsName); + init = maker.NewClass(null, List.nil(), maker.Ident(fieldsName), List.nil(), null); + } else { + returnType = chainDots(field, "java", "lang", "String"); + init = maker.Literal(field.getName()); + } + JCVariableDecl enumField = maker.VarDef(enumValueMods, ((JCVariableDecl) field.get()).name, returnType, init); + injectField(innerNode, enumField); + } } -} \ No newline at end of file +} diff --git a/test/transform/resource/after-delombok/FieldNameConstantsBasic.java b/test/transform/resource/after-delombok/FieldNameConstantsBasic.java index 4e547aa5..8be45d2b 100644 --- a/test/transform/resource/after-delombok/FieldNameConstantsBasic.java +++ b/test/transform/resource/after-delombok/FieldNameConstantsBasic.java @@ -1,8 +1,12 @@ public class FieldNameConstantsBasic { - protected static final java.lang.String FIELD_I_AM_A_DVD_PLAYER = "iAmADvdPlayer"; - public static final java.lang.String FIELD_BUT_PRINT_ME_PLEASE = "butPrintMePlease"; String iAmADvdPlayer; int $skipMe; static double skipMeToo; + int andMe; String butPrintMePlease; + @java.lang.SuppressWarnings("all") + static final class Fields { + public static final java.lang.String butPrintMePlease = "butPrintMePlease"; + public static final java.lang.String iAmADvdPlayer = "iAmADvdPlayer"; + } } diff --git a/test/transform/resource/after-delombok/FieldNameConstantsConfigKeys.java b/test/transform/resource/after-delombok/FieldNameConstantsConfigKeys.java index c71b9264..4c2a53e3 100644 --- a/test/transform/resource/after-delombok/FieldNameConstantsConfigKeys.java +++ b/test/transform/resource/after-delombok/FieldNameConstantsConfigKeys.java @@ -1,4 +1,7 @@ public class FieldNameConstantsConfigKeys { - public static final java.lang.String I_AM_A_DVD_PLAYER_SFX = "iAmADvdPlayer"; String iAmADvdPlayer; -} + @java.lang.SuppressWarnings("all") + public static final class Foobar { + public static final java.lang.String iAmADvdPlayer = "iAmADvdPlayer"; + } +} \ No newline at end of file diff --git a/test/transform/resource/after-delombok/FieldNameConstantsEnum.java b/test/transform/resource/after-delombok/FieldNameConstantsEnum.java new file mode 100644 index 00000000..c3fbf0c2 --- /dev/null +++ b/test/transform/resource/after-delombok/FieldNameConstantsEnum.java @@ -0,0 +1,10 @@ +public class FieldNameConstantsEnum { + String iAmADvdPlayer; + int $dontSkipMe; + static double alsoDontSkipMe; + int butSkipMe; + @java.lang.SuppressWarnings("all") + public enum TypeTest { + iAmADvdPlayer, $dontSkipMe, alsoDontSkipMe; + } +} diff --git a/test/transform/resource/after-delombok/FieldNameConstantsWeird.java b/test/transform/resource/after-delombok/FieldNameConstantsWeird.java deleted file mode 100644 index 6940f628..00000000 --- a/test/transform/resource/after-delombok/FieldNameConstantsWeird.java +++ /dev/null @@ -1,6 +0,0 @@ -public class FieldNameConstantsWeird { - public static final java.lang.String FIELD_AZ = "A"; - String iAmADvdPlayer; - String X; - String A; -} diff --git a/test/transform/resource/after-ecj/FieldNameConstantsBasic.java b/test/transform/resource/after-ecj/FieldNameConstantsBasic.java index f77203ba..674dd602 100644 --- a/test/transform/resource/after-ecj/FieldNameConstantsBasic.java +++ b/test/transform/resource/after-ecj/FieldNameConstantsBasic.java @@ -1,15 +1,23 @@ import lombok.experimental.FieldNameConstants; import lombok.AccessLevel; -public @FieldNameConstants class FieldNameConstantsBasic { - public static final java.lang.String FIELD_BUT_PRINT_ME_PLEASE = "butPrintMePlease"; - protected static final java.lang.String FIELD_I_AM_A_DVD_PLAYER = "iAmADvdPlayer"; - @FieldNameConstants(level = AccessLevel.PROTECTED) String iAmADvdPlayer; +public @FieldNameConstants(level = AccessLevel.PACKAGE) class FieldNameConstantsBasic { + static final @java.lang.SuppressWarnings("all") class Fields { + public static final java.lang.String butPrintMePlease = "butPrintMePlease"; + public static final java.lang.String iAmADvdPlayer = "iAmADvdPlayer"; + () { + } + private @java.lang.SuppressWarnings("all") Fields() { + super(); + } + } + String iAmADvdPlayer; int $skipMe; static double skipMeToo; + @FieldNameConstants.Exclude int andMe; String butPrintMePlease; () { } public FieldNameConstantsBasic() { super(); } -} +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/FieldNameConstantsConfigKeys.java b/test/transform/resource/after-ecj/FieldNameConstantsConfigKeys.java index 44629ee5..7eb97364 100644 --- a/test/transform/resource/after-ecj/FieldNameConstantsConfigKeys.java +++ b/test/transform/resource/after-ecj/FieldNameConstantsConfigKeys.java @@ -1,9 +1,14 @@ public @lombok.experimental.FieldNameConstants class FieldNameConstantsConfigKeys { - public static final java.lang.String I_AM_A_DVD_PLAYER_SFX = "iAmADvdPlayer"; - String iAmADvdPlayer; - () { + public static final @java.lang.SuppressWarnings("all") class Foobar { + public static final java.lang.String iAmADvdPlayer = "iAmADvdPlayer"; + () { + } + private @java.lang.SuppressWarnings("all") Foobar() { + super(); + } } + String iAmADvdPlayer; public FieldNameConstantsConfigKeys() { super(); } -} +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/FieldNameConstantsEnum.java b/test/transform/resource/after-ecj/FieldNameConstantsEnum.java new file mode 100644 index 00000000..053a6e7a --- /dev/null +++ b/test/transform/resource/after-ecj/FieldNameConstantsEnum.java @@ -0,0 +1,23 @@ +import lombok.experimental.FieldNameConstants; +import lombok.AccessLevel; +public @FieldNameConstants(onlyExplicitlyIncluded = true,asEnum = true,innerTypeName = "TypeTest") class FieldNameConstantsEnum { + public @java.lang.SuppressWarnings("all") enum TypeTest { + iAmADvdPlayer(), + $dontSkipMe(), + alsoDontSkipMe(), + private @java.lang.SuppressWarnings("all") TypeTest() { + super(); + } + () { + } + } + @FieldNameConstants.Include String iAmADvdPlayer; + @FieldNameConstants.Include int $dontSkipMe; + static @FieldNameConstants.Include double alsoDontSkipMe; + int butSkipMe; + () { + } + public FieldNameConstantsEnum() { + super(); + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/FieldNameConstantsWeird.java b/test/transform/resource/after-ecj/FieldNameConstantsWeird.java deleted file mode 100644 index 9958f664..00000000 --- a/test/transform/resource/after-ecj/FieldNameConstantsWeird.java +++ /dev/null @@ -1,13 +0,0 @@ -import lombok.experimental.FieldNameConstants; -import lombok.AccessLevel; -public @FieldNameConstants class FieldNameConstantsWeird { - public static final java.lang.String FIELD_AZ = "A"; - @FieldNameConstants(level = AccessLevel.NONE) String iAmADvdPlayer; - @FieldNameConstants(prefix = "") String X; - @FieldNameConstants(suffix = "Z") String A; - () { - } - public FieldNameConstantsWeird() { - super(); - } -} diff --git a/test/transform/resource/before/FieldNameConstantsBasic.java b/test/transform/resource/before/FieldNameConstantsBasic.java index 1bc15d84..2842dea3 100644 --- a/test/transform/resource/before/FieldNameConstantsBasic.java +++ b/test/transform/resource/before/FieldNameConstantsBasic.java @@ -1,11 +1,11 @@ import lombok.experimental.FieldNameConstants; import lombok.AccessLevel; -@FieldNameConstants +@FieldNameConstants(level = AccessLevel.PACKAGE) public class FieldNameConstantsBasic { - @FieldNameConstants(level = AccessLevel.PROTECTED) String iAmADvdPlayer; int $skipMe; static double skipMeToo; + @FieldNameConstants.Exclude int andMe; String butPrintMePlease; } diff --git a/test/transform/resource/before/FieldNameConstantsConfigKeys.java b/test/transform/resource/before/FieldNameConstantsConfigKeys.java index ab8e3091..5595222c 100644 --- a/test/transform/resource/before/FieldNameConstantsConfigKeys.java +++ b/test/transform/resource/before/FieldNameConstantsConfigKeys.java @@ -1,5 +1,4 @@ -//CONF: lombok.fieldNameConstants.prefix = -//CONF: lombok.fieldNameConstants.suffix = _SFX +//CONF: lombok.fieldNameConstants.innerTypeName = Foobar @lombok.experimental.FieldNameConstants public class FieldNameConstantsConfigKeys { diff --git a/test/transform/resource/before/FieldNameConstantsEnum.java b/test/transform/resource/before/FieldNameConstantsEnum.java new file mode 100644 index 00000000..d56bb280 --- /dev/null +++ b/test/transform/resource/before/FieldNameConstantsEnum.java @@ -0,0 +1,13 @@ +import lombok.experimental.FieldNameConstants; +import lombok.AccessLevel; + +@FieldNameConstants(onlyExplicitlyIncluded = true, asEnum = true, innerTypeName = "TypeTest") +public class FieldNameConstantsEnum { + @FieldNameConstants.Include + String iAmADvdPlayer; + @FieldNameConstants.Include + int $dontSkipMe; + @FieldNameConstants.Include + static double alsoDontSkipMe; + int butSkipMe; +} diff --git a/test/transform/resource/before/FieldNameConstantsWeird.java b/test/transform/resource/before/FieldNameConstantsWeird.java deleted file mode 100644 index 74ec299a..00000000 --- a/test/transform/resource/before/FieldNameConstantsWeird.java +++ /dev/null @@ -1,12 +0,0 @@ -import lombok.experimental.FieldNameConstants; -import lombok.AccessLevel; - -@FieldNameConstants -public class FieldNameConstantsWeird { - @FieldNameConstants(level = AccessLevel.NONE) - String iAmADvdPlayer; - @FieldNameConstants(prefix = "") - String X; - @FieldNameConstants(suffix = "Z") - String A; -} diff --git a/test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages b/test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages deleted file mode 100644 index 02a38d58..00000000 --- a/test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages +++ /dev/null @@ -1 +0,0 @@ -9 Not generating constant for this field: The name of the constant would be equal to the name of this field. diff --git a/test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages b/test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages deleted file mode 100644 index 02a38d58..00000000 --- a/test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages +++ /dev/null @@ -1 +0,0 @@ -9 Not generating constant for this field: The name of the constant would be equal to the name of this field. -- cgit From 35fe755b87f194621b5f328e4773f1f406f8ccc9 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Mon, 20 Aug 2018 23:44:31 +0200 Subject: bugfix for the new ‘find the next priority’ system committed earlier; it should also find next priority for the astvisitors (only the annotation visitors were being considered). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/lombok/core/configuration/StringConfigurationSource.java | 1 - src/core/lombok/eclipse/HandlerLibrary.java | 8 +++++--- src/core/lombok/eclipse/TransformEclipseAST.java | 8 ++------ src/core/lombok/javac/apt/LombokProcessor.java | 6 +++--- test/core/src/lombok/RunTestsViaEcj.java | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) (limited to 'test') diff --git a/src/core/lombok/core/configuration/StringConfigurationSource.java b/src/core/lombok/core/configuration/StringConfigurationSource.java index dd2f0319..abf6eea0 100644 --- a/src/core/lombok/core/configuration/StringConfigurationSource.java +++ b/src/core/lombok/core/configuration/StringConfigurationSource.java @@ -67,7 +67,6 @@ public class StringConfigurationSource implements ConfigurationSource { list.add(new ListModification(value, add)); } }); - return new StringConfigurationSource(values); } diff --git a/src/core/lombok/eclipse/HandlerLibrary.java b/src/core/lombok/eclipse/HandlerLibrary.java index 75a22f03..0e72fb38 100644 --- a/src/core/lombok/eclipse/HandlerLibrary.java +++ b/src/core/lombok/eclipse/HandlerLibrary.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 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 @@ -32,7 +32,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -247,8 +246,10 @@ public class HandlerLibrary { /** * Will call all registered {@link EclipseASTVisitor} instances. */ - public void callASTVisitors(EclipseAST ast, long priority, boolean isCompleteParse) { + public long callASTVisitors(EclipseAST ast, long priority, boolean isCompleteParse) { + long nearestPriority = Long.MAX_VALUE; for (VisitorContainer container : visitorHandlers) { + if (priority < container.getPriority()) nearestPriority = Math.min(container.getPriority(), nearestPriority); if (!isCompleteParse && container.deferUntilPostDiet()) continue; if (priority != container.getPriority()) continue; try { @@ -258,5 +259,6 @@ public class HandlerLibrary { String.format("Lombok visitor handler %s failed", container.visitor.getClass()), t); } } + return nearestPriority; } } diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java index 24e24495..323fc171 100644 --- a/src/core/lombok/eclipse/TransformEclipseAST.java +++ b/src/core/lombok/eclipse/TransformEclipseAST.java @@ -24,8 +24,6 @@ package lombok.eclipse; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Field; -import java.util.HashSet; -import java.util.Set; import lombok.ConfigurationKeys; import lombok.core.LombokConfiguration; @@ -190,14 +188,12 @@ public class TransformEclipseAST { public void go() { long nextPriority = Long.MIN_VALUE; for (Long d : handlers.getPriorities()) { - if (nextPriority > d) { - continue; - } + if (nextPriority > d) continue; AnnotationVisitor visitor = new AnnotationVisitor(d); ast.traverse(visitor); // if no visitor interested for this AST, nextPriority would be MAX_VALUE and we bail out immediatetly nextPriority = visitor.getNextPriority(); - handlers.callASTVisitors(ast, d, ast.isCompleteParse()); + nextPriority = Math.min(nextPriority, handlers.callASTVisitors(ast, d, ast.isCompleteParse())); } } diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java index 04b494bf..89608546 100644 --- a/src/core/lombok/javac/apt/LombokProcessor.java +++ b/src/core/lombok/javac/apt/LombokProcessor.java @@ -166,13 +166,13 @@ public class LombokProcessor extends AbstractProcessor { if (!(originalFiler instanceof InterceptingJavaFileManager)) { final Messager messager = processingEnv.getMessager(); DiagnosticsReceiver receiver = new MessagerDiagnosticsReceiver(messager); - + JavaFileManager newFilerManager = new InterceptingJavaFileManager(originalFiler, receiver); ht.put(key, newFilerManager); Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager"); filerFileManagerField.setAccessible(true); filerFileManagerField.set(javacFiler, newFilerManager); - + replaceFileManagerJdk9(context, newFilerManager); } } catch (Exception e) { @@ -429,7 +429,7 @@ public class LombokProcessor extends AbstractProcessor { } /** - * This class returns the given filer as a JavacFiler. In case the case that the filer is no + * This class returns the given filer as a JavacFiler. In case the filer is no * JavacFiler (e.g. the Gradle IncrementalFiler), its "delegate" field is used to get the JavacFiler * (directly or through a delegate field again) */ diff --git a/test/core/src/lombok/RunTestsViaEcj.java b/test/core/src/lombok/RunTestsViaEcj.java index 4e6a6a55..3efe38f5 100644 --- a/test/core/src/lombok/RunTestsViaEcj.java +++ b/test/core/src/lombok/RunTestsViaEcj.java @@ -122,7 +122,7 @@ public class RunTestsViaEcj extends AbstractRunTests { CompilationUnitDeclaration cud = compilationUnit_.get(); - if (cud == null) result.append("---- NO CompilationUnit provided by ecj ----"); + if (cud == null) result.append("---- No CompilationUnit provided by ecj ----"); else result.append(cud.toString()); return true; -- cgit From ca35539f7cd7967bfd8518d0ad0b0015bdd40cfc Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Tue, 21 Aug 2018 00:01:40 +0200 Subject: [fixes #1812] `@Singular` marked collections which nevertheless somehow ended up null would cause an NPE during `toBuilder()` invocations. --- .../eclipse/handlers/EclipseHandlerUtil.java | 26 +++++++++ .../lombok/eclipse/handlers/HandleBuilder.java | 34 +++++++++--- .../eclipse/handlers/HandleEqualsAndHashCode.java | 26 --------- src/core/lombok/javac/handlers/HandleBuilder.java | 28 +++++++--- .../BuilderSingularToBuilderWithNull.java | 63 ++++++++++++++++++++++ .../after-delombok/BuilderWithToBuilder.java | 2 +- .../BuilderSingularToBuilderWithNull.java | 57 ++++++++++++++++++++ .../resource/after-ecj/BuilderWithToBuilder.java | 2 +- .../before/BuilderSingularToBuilderWithNull.java | 10 ++++ 9 files changed, 206 insertions(+), 42 deletions(-) create mode 100644 test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java create mode 100644 test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java create mode 100644 test/transform/resource/before/BuilderSingularToBuilderWithNull.java (limited to 'test') diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 2dce285c..87df6d1b 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -24,6 +24,7 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.EclipseAugments.*; +import static lombok.eclipse.handlers.EclipseHandlerUtil.setGeneratedBy; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -1862,4 +1863,29 @@ public class EclipseHandlerUtil { String p = typeDecl.superclass.toString(); return p.equals("Object") || p.equals("java.lang.Object"); } + + public static NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) { + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long)pS << 32 | pE; + + NameReference ref; + + if (varNames.length > 1) ref = new QualifiedNameReference(varNames, new long[varNames.length], pS, pE); + else ref = new SingleNameReference(varNames[0], p); + setGeneratedBy(ref, source); + return ref; + } + + public static TypeReference generateQualifiedTypeRef(ASTNode source, char[]... varNames) { + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long)pS << 32 | pE; + + TypeReference ref; + + long[] poss = Eclipse.poss(source, varNames.length); + if (varNames.length > 1) ref = new QualifiedTypeReference(varNames, poss); + else ref = new SingleTypeReference(varNames[0], p); + setGeneratedBy(ref, source); + return ref; + } } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 4301e51a..9a069c58 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -40,6 +40,7 @@ import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; @@ -47,6 +48,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; @@ -492,6 +494,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { } } + private static final char[] EMPTY_LIST = "emptyList".toCharArray(); private MethodDeclaration generateToBuilderMethod(String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List builderFields, boolean fluent, ASTNode source) { // return new ThingieBuilder().setA(this.a).setB(this.b); @@ -511,19 +514,34 @@ public class HandleBuilder extends EclipseAnnotationHandler { for (BuilderFieldData bfd : builderFields) { char[] setterName = fluent ? bfd.name : HandlerUtil.buildAccessorName("set", new String(bfd.name)).toCharArray(); MessageSend ms = new MessageSend(); + Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2]; + if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray(); - FieldReference fr = new FieldReference(fieldName, 0); - fr.receiver = new ThisReference(0, 0); - ms.arguments = new Expression[] {fr}; + for (int i = 0; i < tgt.length; i++) { + FieldReference fr = new FieldReference(fieldName, 0); + fr.receiver = new ThisReference(0, 0); + tgt[i] = fr; + } } else { String obtainName = bfd.obtainVia.method(); boolean obtainIsStatic = bfd.obtainVia.isStatic(); - MessageSend obtainExpr = new MessageSend(); - obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0); - obtainExpr.selector = obtainName.toCharArray(); - if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)}; - ms.arguments = new Expression[] {obtainExpr}; + for (int i = 0; i < tgt.length; i++) { + MessageSend obtainExpr = new MessageSend(); + obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0); + obtainExpr.selector = obtainName.toCharArray(); + if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)}; + tgt[i] = obtainExpr; + } + } + if (bfd.singularData == null) { + ms.arguments = tgt; + } else { + Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); + MessageSend emptyList = new MessageSend(); + emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray()); + emptyList.selector = EMPTY_LIST; + ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])}; } ms.receiver = receiver; ms.selector = setterName; diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index c99b9b5f..6945e5d9 100644 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -69,7 +69,6 @@ import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; -import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; @@ -819,29 +818,4 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler 1) ref = new QualifiedNameReference(varNames, new long[varNames.length], pS, pE); - else ref = new SingleNameReference(varNames[0], p); - setGeneratedBy(ref, source); - return ref; - } - - public TypeReference generateQualifiedTypeRef(ASTNode source, char[]... varNames) { - int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long)pS << 32 | pE; - - TypeReference ref; - - long[] poss = Eclipse.poss(source, varNames.length); - if (varNames.length > 1) ref = new QualifiedTypeReference(varNames, poss); - else ref = new SingleTypeReference(varNames[0], p); - setGeneratedBy(ref, source); - return ref; - } } diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index f04ea8b1..edf6f2ae 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -492,18 +492,34 @@ public class HandleBuilder extends JavacAnnotationHandler { JCExpression invoke = call; for (BuilderFieldData bfd : builderFields) { Name setterName = fluent ? bfd.name : type.toName(HandlerUtil.buildAccessorName("set", bfd.name.toString())); - JCExpression arg; + JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2]; if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { - arg = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field())); + for (int i = 0; i < tgt.length; i++) { + tgt[i] = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field())); + } } else { if (bfd.obtainVia.isStatic()) { - JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method())); - arg = maker.Apply(List.nil(), c, List.of(maker.Ident(type.toName("this")))); + for (int i = 0; i < tgt.length; i++) { + JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method())); + tgt[i] = maker.Apply(List.nil(), c, List.of(maker.Ident(type.toName("this")))); + } } else { - JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(bfd.obtainVia.method())); - arg = maker.Apply(List.nil(), c, List.nil()); + for (int i = 0; i < tgt.length; i++) { + JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(bfd.obtainVia.method())); + tgt[i] = maker.Apply(List.nil(), c, List.nil()); + } } } + + JCExpression arg; + if (bfd.singularData == null) { + arg = tgt[0]; + } else { + JCExpression eqNull = maker.Binary(CTC_EQUAL, tgt[0], maker.Literal(CTC_BOT, null)); + JCExpression emptyList = maker.Apply(List.nil(), chainDots(type, "java", "util", "Collections", "emptyList"), List.nil()); + arg = maker.Conditional(eqNull, emptyList, tgt[1]); + } + invoke = maker.Apply(List.nil(), maker.Select(invoke, setterName), List.of(arg)); } JCStatement statement = maker.Return(invoke); diff --git a/test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java b/test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java new file mode 100644 index 00000000..1f472438 --- /dev/null +++ b/test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java @@ -0,0 +1,63 @@ +class BuilderSingularToBuilderWithNull { + private java.util.List elems; + public static void test() { + new BuilderSingularToBuilderWithNull(null).toBuilder(); + } + @java.lang.SuppressWarnings("all") + BuilderSingularToBuilderWithNull(final java.util.List elems) { + this.elems = elems; + } + @java.lang.SuppressWarnings("all") + public static class BuilderSingularToBuilderWithNullBuilder { + @java.lang.SuppressWarnings("all") + private java.util.ArrayList elems; + @java.lang.SuppressWarnings("all") + BuilderSingularToBuilderWithNullBuilder() { + } + @java.lang.SuppressWarnings("all") + public BuilderSingularToBuilderWithNullBuilder elem(final String elem) { + if (this.elems == null) this.elems = new java.util.ArrayList(); + this.elems.add(elem); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingularToBuilderWithNullBuilder elems(final java.util.Collection elems) { + if (this.elems == null) this.elems = new java.util.ArrayList(); + this.elems.addAll(elems); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingularToBuilderWithNullBuilder clearElems() { + if (this.elems != null) this.elems.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingularToBuilderWithNull build() { + java.util.List elems; + switch (this.elems == null ? 0 : this.elems.size()) { + case 0: + elems = java.util.Collections.emptyList(); + break; + case 1: + elems = java.util.Collections.singletonList(this.elems.get(0)); + break; + default: + elems = java.util.Collections.unmodifiableList(new java.util.ArrayList(this.elems)); + } + return new BuilderSingularToBuilderWithNull(elems); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "BuilderSingularToBuilderWithNull.BuilderSingularToBuilderWithNullBuilder(elems=" + this.elems + ")"; + } + } + @java.lang.SuppressWarnings("all") + public static BuilderSingularToBuilderWithNullBuilder builder() { + return new BuilderSingularToBuilderWithNullBuilder(); + } + @java.lang.SuppressWarnings("all") + public BuilderSingularToBuilderWithNullBuilder toBuilder() { + return new BuilderSingularToBuilderWithNullBuilder().elems(this.elems == null ? java.util.Collections.emptyList() : this.elems); + } +} diff --git a/test/transform/resource/after-delombok/BuilderWithToBuilder.java b/test/transform/resource/after-delombok/BuilderWithToBuilder.java index 46387f0f..b644a16f 100644 --- a/test/transform/resource/after-delombok/BuilderWithToBuilder.java +++ b/test/transform/resource/after-delombok/BuilderWithToBuilder.java @@ -86,7 +86,7 @@ class BuilderWithToBuilder { } @java.lang.SuppressWarnings("all") public BuilderWithToBuilderBuilder toBuilder() { - return new BuilderWithToBuilderBuilder().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(this.bars); + return new BuilderWithToBuilderBuilder().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(this.bars == null ? java.util.Collections.emptyList() : this.bars); } } class ConstructorWithToBuilder { diff --git a/test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java b/test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java new file mode 100644 index 00000000..7265e17a --- /dev/null +++ b/test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java @@ -0,0 +1,57 @@ +import lombok.Singular; +@lombok.Builder(toBuilder = true) class BuilderSingularToBuilderWithNull { + public static @java.lang.SuppressWarnings("all") class BuilderSingularToBuilderWithNullBuilder { + private @java.lang.SuppressWarnings("all") java.util.ArrayList elems; + @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder() { + super(); + } + public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder elem(String elem) { + if ((this.elems == null)) + this.elems = new java.util.ArrayList(); + this.elems.add(elem); + return this; + } + public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder elems(java.util.Collection elems) { + if ((this.elems == null)) + this.elems = new java.util.ArrayList(); + this.elems.addAll(elems); + return this; + } + public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder clearElems() { + if ((this.elems != null)) + this.elems.clear(); + return this; + } + public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNull build() { + java.util.List elems; + switch (((this.elems == null) ? 0 : this.elems.size())) { + case 0 : + elems = java.util.Collections.emptyList(); + break; + case 1 : + elems = java.util.Collections.singletonList(this.elems.get(0)); + break; + default : + elems = java.util.Collections.unmodifiableList(new java.util.ArrayList(this.elems)); + } + return new BuilderSingularToBuilderWithNull(elems); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("BuilderSingularToBuilderWithNull.BuilderSingularToBuilderWithNullBuilder(elems=" + this.elems) + ")"); + } + } + private @Singular java.util.List elems; + public static void test() { + new BuilderSingularToBuilderWithNull(null).toBuilder(); + } + @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNull(final java.util.List elems) { + super(); + this.elems = elems; + } + public static @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder builder() { + return new BuilderSingularToBuilderWithNullBuilder(); + } + public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder toBuilder() { + return new BuilderSingularToBuilderWithNullBuilder().elems(((this.elems == null) ? java.util.Collections.emptyList() : this.elems)); + } +} diff --git a/test/transform/resource/after-ecj/BuilderWithToBuilder.java b/test/transform/resource/after-ecj/BuilderWithToBuilder.java index d304293c..b9cc27dd 100644 --- a/test/transform/resource/after-ecj/BuilderWithToBuilder.java +++ b/test/transform/resource/after-ecj/BuilderWithToBuilder.java @@ -74,7 +74,7 @@ import lombok.Builder; return new BuilderWithToBuilderBuilder(); } public @java.lang.SuppressWarnings("all") BuilderWithToBuilderBuilder toBuilder() { - return new BuilderWithToBuilderBuilder().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(this.bars); + return new BuilderWithToBuilderBuilder().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(((this.bars == null) ? java.util.Collections.emptyList() : this.bars)); } } @lombok.experimental.Accessors(prefix = "m") class ConstructorWithToBuilder { diff --git a/test/transform/resource/before/BuilderSingularToBuilderWithNull.java b/test/transform/resource/before/BuilderSingularToBuilderWithNull.java new file mode 100644 index 00000000..92a102cb --- /dev/null +++ b/test/transform/resource/before/BuilderSingularToBuilderWithNull.java @@ -0,0 +1,10 @@ +import lombok.Singular; + +@lombok.Builder(toBuilder = true) +class BuilderSingularToBuilderWithNull { + @Singular private java.util.List elems; + + public static void test() { + new BuilderSingularToBuilderWithNull(null).toBuilder(); + } +} -- cgit